Deploy a live Python view
A live Python view runs notebook code on the server for each marimo session. Deploy it with the same care as an application that can read the notebook's files, packages, databases, network, and credentials.
Start an authenticated process
Store the marimo token in a file readable by the application user:
uvx --with marimo-studio marimo run /srv/analysis/analysis.py \
--sandbox \
--headless \
--host 127.0.0.1 \
--port 8000 \
--token-password-file /run/secrets/marimo-tokenBinding to 127.0.0.1 keeps the process behind the local reverse proxy. The token protects the marimo and Studio routes with session-based authentication. Keep the token file outside the repository and rotate it through the deployment secret manager.
--sandbox installs the notebook's PEP 723 dependencies with uv. It manages the Python environment and does not isolate untrusted notebook code. Build the environment ahead of time when startup cannot depend on package indexes or when production policy requires a reviewed lock and image.
Put a reverse proxy in front
A reverse proxy accepts public requests, applies access and transport policy, then forwards them to the Studio process. Terminate TLS, which encrypts the public connection, at the proxy. Forward HTTP plus WebSocket connections so live notebook updates continue to work. Studio builds every URL from the requested path, so the proxy may rewrite Host. Restrict the upstream port to trusted local or private-network clients.
Serve beneath a path prefix
Studio writes each page, redirect, and live-connection URL relative to the URL the browser requested, so a deployment can publish the notebook beneath a path prefix such as https://example.com/occupancy/. Configure marimo for the way the proxy forwards that prefix:
Proxy forwards https://example.com/occupancy/studio/ as | marimo option |
|---|---|
/occupancy/studio/ | --base-url /occupancy |
/studio/ | No base URL |
When the proxy keeps the prefix, pass the same value to marimo:
uvx --with marimo-studio marimo run /srv/analysis/analysis.py \
--sandbox \
--headless \
--host 127.0.0.1 \
--port 8000 \
--base-url /occupancy \
--token-password-file /run/secrets/marimo-tokenWhen the proxy strips the prefix, start marimo with no base URL. IDE servers publish a local port this way, and the prefix can change between sessions. Posit Workbench publishes ports beneath /s/<session>/p/<port>/ and strips that prefix. jupyter-server-proxy and code-server strip it on /proxy/<port>/. Their /proxy/absolute/<port>/ and /absproxy/<port>/ routes keep the prefix and pair with --base-url.
Open the public root with a trailing slash, such as https://example.com/occupancy/. Browsers resolve relative references against the requested directory, so configure the proxy to redirect https://example.com/occupancy to that directory.
marimo's password form submits to the server root, which a stripping proxy does not forward. Open the notebook once with ?access_token=<token>. Studio starts the session and removes the token with a redirect beneath the prefix, in edit and run mode. That first request carries the token in its query string, so proxy and server access logs can record it. Redact access_token from those logs. Starting marimo with --no-token also works, but it lets every account on the machine reach the notebook through its local port. Keep a token on shared hosts.
Use --allow-origins when browser clients must connect from another explicit origin. Keep the list to origins that should receive notebook sessions.
Embed the Studio edit workspace
Set MARIMO_STUDIO_ALLOWED_EMBED_ORIGINS when another site embeds Studio's edit workspace. The value is a comma-separated list of exact HTTP or HTTPS origins:
MARIMO_STUDIO_ALLOWED_EMBED_ORIGINS=http://localhost:55021,https://notebooks.example.com \
marimo edit /srv/analysis/analysis.py --headless --port 8000Studio adds these origins to the frame-ancestors Content Security Policy directive on the workspace and native editor documents. 'self' remains present for Studio's internal editor iframe. Studio normalizes host casing, default ports, and an optional trailing slash, then removes duplicates. The configuration accepts at most 32 origin entries and 4,096 UTF-8 bytes. An invalid or oversized value stops server startup and names the rejected setting.
Allow a parent origin only when you trust its pages to present Studio controls. An allowed parent can position the authenticated workspace inside its own interface and attempt clickjacking, where a user is misled into interacting with the framed application. The allowlist changes framing policy. marimo authentication remains required. marimo token sessions use SameSite=Lax cookies. For a same-site parent on another origin, authenticate on the Studio origin before loading the workspace in the frame. A cross-site parent needs an external authentication layer designed for third-party iframe contexts.
This setting controls which parent documents may frame Studio. marimo's --allow-origins option controls request origins for browser clients.
Run as an ASGI application
ASGI is the standard interface between asynchronous Python web applications and servers. Studio exposes an environment-configured ASGI entry point:
MARIMO_STUDIO_NOTEBOOK=/srv/analysis/analysis.py \
uvicorn marimo_studio.asgi:app \
--host 127.0.0.1 \
--port 8000 \
--lifespan onThe equivalent Python API is:
from marimo_studio import create_asgi_app
app = create_asgi_app("/srv/analysis/analysis.py")The application lifespan opens Studio services and closes notebook sessions, provider operations, and background tasks during shutdown. Start with one ASGI worker for one notebook application because live session and presentation authority is process-local.
Cookie-authenticated reverse proxies can require same-origin Server runtime documents. Set the trusted runtime policy in the process that serves Studio:
MARIMO_STUDIO_TRUSTED_SERVER_RUNTIME=1 \
MARIMO_STUDIO_NOTEBOOK=/srv/analysis/analysis.py \
uvicorn marimo_studio.asgi:app --host 127.0.0.1 --port 8000 --lifespan onThe policy covers the view document, the workspace preview iframe, and the ASGI application path. It is process-wide and is read when composition starts, so restart the process after changing it. Use it when the notebook and authored view code are trusted with the authenticated host. Server-authored code can access host cookies, local storage, the parent document, and same-origin requests. Browser and Prepared runtimes retain opaque-origin isolation.
create_asgi_app() has no token configuration argument. Put this form behind an access-controlled reverse proxy or compose it into an application that owns authentication before exposing it beyond a trusted network.
Check readiness and shutdown
Use the marimo health endpoint for the process check:
curl --fail http://127.0.0.1:8000/healthFor a prefixed deployment, request /occupancy/health. Keep health checks on the trusted side of the proxy.
Send the process its normal termination signal and allow the application lifespan to finish. A forced stop can interrupt sessions, builds, and artifact leases.
After deployment, open the default and one named view, authenticate, change a notebook control, reload the page, and inspect browser diagnostics. Use Navigate and preserve state when the deployment must replay Python sessions across reloads.