Deployment Platforms
Deploy marimo notebooks to production environments using cloud platforms, Docker containers, Kubernetes, and more. This guide covers best practices for production deployments across various platforms.
Docker Deployment
Official Docker Images
marimo provides official Docker images for quick deployment:
Slim (Base Image)
Data Science Stack
SQL Support
FROM ghcr.io/marimo-team/marimo:latest
# Copy your notebooks
COPY notebooks/ /app/notebooks/
WORKDIR /app
EXPOSE 8080
CMD [ "marimo" , "run" , "notebooks/app.py" , "--host" , "0.0.0.0" , "--port" , "8080" , "--headless" ]
Custom Dockerfile
Build a custom image with your dependencies:
FROM python:3.11-slim
# Install system dependencies if needed
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install uv for fast package installation
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# Set working directory
WORKDIR /app
# Copy requirements
COPY requirements.txt .
# Install Python dependencies
RUN uv pip install --system --no-cache -r requirements.txt
# Copy application
COPY . .
# Create non-root user
RUN useradd -m -u 1000 appuser && \
chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD curl -f http://localhost:8080/health || exit 1
# Run application
CMD [ "marimo" , "run" , "app.py" , \
"--host" , "0.0.0.0" , \
"--port" , "8080" , \
"--headless" , \
"--no-token" ]
Docker Compose
Orchestrate multi-container deployments:
version : '3.8'
services :
marimo :
build : .
ports :
- "8080:8080"
environment :
- MARIMO_SKIP_UPDATE_CHECK=1
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
volumes :
- ./data:/app/data
- ./notebooks:/app/notebooks
depends_on :
- db
restart : unless-stopped
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost:8080/health" ]
interval : 30s
timeout : 3s
retries : 3
db :
image : postgres:15-alpine
environment :
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes :
- postgres_data:/var/lib/postgresql/data
restart : unless-stopped
volumes :
postgres_data :
Run with:
Railway
Deploy to Railway with one click:
Create railway.toml
[ build ]
builder = "nixpacks"
[ deploy ]
startCommand = "marimo run app.py --host 0.0.0.0 --port $PORT --headless --no-token"
healthcheckPath = "/health"
healthcheckTimeout = 100
restartPolicyType = "on_failure"
Add requirements.txt
marimo>=0.12.0
pandas
altair
Deploy
Connect your GitHub repo to Railway and deploy automatically on push.
See the Railway deployment guide for details.
Hugging Face Spaces
Deploy as a Hugging Face Space:
Create README.md
---
title : My marimo App
emoji : 📊
colorFrom : blue
colorTo : purple
sdk : docker
app_port : 8080
---
Create Dockerfile
FROM ghcr.io/marimo-team/marimo:latest
COPY app.py /app/
WORKDIR /app
EXPOSE 8080
CMD [ "marimo" , "run" , "app.py" , "--host" , "0.0.0.0" , "--port" , "8080" , "--headless" , "--no-token" ]
Push to Hugging Face
Upload files to a new Space and it will build automatically.
See the Hugging Face guide .
Google Cloud Run
Deploy serverless on Google Cloud:
# Build and push container
gcloud builds submit --tag gcr.io/PROJECT_ID/marimo-app
# Deploy to Cloud Run
gcloud run deploy marimo-app \
--image gcr.io/PROJECT_ID/marimo-app \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--port 8080 \
--memory 2Gi \
--cpu 2 \
--timeout 3600 \
--set-env-vars MARIMO_SKIP_UPDATE_CHECK= 1
AWS ECS/Fargate
Deploy on AWS Elastic Container Service:
{
"family" : "marimo-app" ,
"networkMode" : "awsvpc" ,
"requiresCompatibilities" : [ "FARGATE" ],
"cpu" : "1024" ,
"memory" : "2048" ,
"containerDefinitions" : [
{
"name" : "marimo" ,
"image" : "YOUR_ECR_REPO/marimo-app:latest" ,
"portMappings" : [
{
"containerPort" : 8080 ,
"protocol" : "tcp"
}
],
"environment" : [
{
"name" : "MARIMO_SKIP_UPDATE_CHECK" ,
"value" : "1"
}
],
"healthCheck" : {
"command" : [ "CMD-SHELL" , "curl -f http://localhost:8080/health || exit 1" ],
"interval" : 30 ,
"timeout" : 5 ,
"retries" : 3
}
}
]
}
Azure Container Instances
az container create \
--resource-group myResourceGroup \
--name marimo-app \
--image myregistry.azurecr.io/marimo-app:latest \
--cpu 2 \
--memory 4 \
--registry-login-server myregistry.azurecr.io \
--registry-username $USERNAME \
--registry-password $PASSWORD \
--dns-name-label marimo-app \
--ports 8080 \
--environment-variables MARIMO_SKIP_UPDATE_CHECK= 1
Kubernetes Deployment
Basic Deployment
Deploy to Kubernetes cluster:
apiVersion : apps/v1
kind : Deployment
metadata :
name : marimo-app
spec :
replicas : 3
selector :
matchLabels :
app : marimo
template :
metadata :
labels :
app : marimo
spec :
containers :
- name : marimo
image : your-registry/marimo-app:latest
ports :
- containerPort : 8080
env :
- name : MARIMO_SKIP_UPDATE_CHECK
value : "1"
- name : DATABASE_URL
valueFrom :
secretKeyRef :
name : marimo-secrets
key : database-url
resources :
requests :
memory : "1Gi"
cpu : "500m"
limits :
memory : "2Gi"
cpu : "1000m"
livenessProbe :
httpGet :
path : /health
port : 8080
initialDelaySeconds : 30
periodSeconds : 10
readinessProbe :
httpGet :
path : /health
port : 8080
initialDelaySeconds : 10
periodSeconds : 5
---
apiVersion : v1
kind : Service
metadata :
name : marimo-service
spec :
selector :
app : marimo
ports :
- protocol : TCP
port : 80
targetPort : 8080
type : LoadBalancer
See the Kubernetes guide for more details.
HPC and Research Computing
Slurm Clusters
Deploy on HPC clusters with Slurm:
#!/bin/bash
#SBATCH --job-name=marimo-app
#SBATCH --output=marimo_%j.log
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --mem=16G
#SBATCH --time=24:00:00
# Load modules
module load python/3.11
# Activate environment
source venv/bin/activate
# Get compute node hostname
HOSTNAME = $( hostname )
PORT = 8080
echo "marimo running on $HOSTNAME : $PORT "
echo "SSH tunnel: ssh -L $PORT : $HOSTNAME : $PORT $USER @login.cluster.edu"
# Run marimo
marimo run app.py --host 0.0.0.0 --port $PORT --headless
See the Slurm deployment guide .
SkyPilot
Deploy to any cloud with SkyPilot:
resources :
cloud : aws
instance_type : t3.medium
disk_size : 50
setup : |
pip install marimo pandas altair
run : |
marimo run app.py --host 0.0.0.0 --port 8080 --headless --no-token
Launch:
sky launch marimo.yaml --cloud aws
See the SkyPilot guide .
Reverse Proxy Configuration
Nginx
Configure nginx as reverse proxy:
upstream marimo {
server 127.0.0.1:8080;
}
server {
listen 80 ;
server_name example.com;
# Redirect to HTTPS
return 301 https://$ server_name $ request_uri ;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://marimo;
proxy_http_version 1.1 ;
# WebSocket support
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
# Headers
proxy_set_header Host $ host ;
proxy_set_header X-Real-IP $ remote_addr ;
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for ;
proxy_set_header X-Forwarded-Proto $ scheme ;
# Timeouts for long-running requests
proxy_read_timeout 3600s ;
proxy_send_timeout 3600s ;
}
}
See the nginx guide for more examples.
Caddy
Use Caddy for automatic HTTPS:
example.com {
reverse_proxy localhost:8080 {
# WebSocket support is automatic
# Timeouts
transport http {
read_timeout 1h
write_timeout 1h
}
}
# Security headers
header {
Strict-Transport-Security "max-age=31536000;"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
}
}
Run:
Environment Configuration
Environment Variables
Configure marimo with environment variables:
# Disable update checks
export MARIMO_SKIP_UPDATE_CHECK = 1
# Set output mode for scripts
export MARIMO_OUTPUT_MODE = quiet
# Configure logging
export MARIMO_LOG_LEVEL = INFO
# Run in secure environment (disables some features)
export MARIMO_IN_SECURE_ENVIRONMENT = true
# Custom paths
export MARIMO_CACHE_DIR = / var / cache / marimo
export MARIMO_CONFIG_DIR = / etc / marimo
Secrets Management
Kubernetes Secrets
Docker Secrets
AWS Secrets Manager
kubectl create secret generic marimo-secrets \
--from-literal=database-url= $DATABASE_URL \
--from-literal=api-key= $API_KEY
Never commit secrets to version control. Use environment variables, secret managers, or mount secrets as files.
Production Best Practices
Security
Use authentication
Enable token authentication or implement custom auth middleware: marimo run app.py --token-password-file /run/secrets/password
Run as non-root user
Create and use a dedicated user in Docker: RUN useradd -m -u 1000 appuser
USER appuser
Enable HTTPS
Use TLS/SSL certificates via reverse proxy (nginx, Caddy).
Configure CORS
Restrict allowed origins: marimo run app.py --allow-origins https://trusted-domain.com
Use security headers
Add via reverse proxy: HSTS, X-Frame-Options, CSP, etc.
Optimize production deployments:
Resource limits : Set appropriate CPU/memory limits
Session TTL : Configure based on expected usage:
marimo run app.py --session-ttl 300 # 5 minutes
Connection pooling : For database-backed apps
Caching : Use Redis/Memcached for shared state
Load balancing : Run multiple replicas behind load balancer
Health checks : Configure liveness/readiness probes
Monitoring : Track metrics with Prometheus, Datadog, etc.
Reliability
# Kubernetes example with proper health checks
livenessProbe :
httpGet :
path : /health
port : 8080
initialDelaySeconds : 60
periodSeconds : 10
timeoutSeconds : 5
failureThreshold : 3
readinessProbe :
httpGet :
path : /health
port : 8080
initialDelaySeconds : 10
periodSeconds : 5
timeoutSeconds : 3
failureThreshold : 2
Monitoring
Monitor your deployment:
# Add custom metrics endpoint
import marimo as mo
from prometheus_client import Counter, generate_latest
request_count = Counter( 'requests_total' , 'Total requests' )
@app.cell
def __ ():
# Your app logic
request_count.inc()
return
# Expose metrics at /metrics
Troubleshooting
Common Issues
Connection refused / timeout
Check firewall rules
Verify port binding (0.0.0.0 not 127.0.0.1)
Check health endpoint: curl http://localhost:8080/health
WebSocket connection failed
Configure reverse proxy for WebSocket upgrade
Check timeout settings (increase for long operations)
Verify SSL/TLS termination handling
Increase container memory limits
Reduce session TTL to clean up faster
Profile memory usage
Use more efficient data structures
Check resource limits (CPU/memory)
Profile application performance
Add caching for expensive operations
Scale horizontally with more replicas
Examples
Complete Production Setup
# Build optimized image
docker build -t marimo-app:prod \
--build-arg PYTHON_VERSION= 3.11 \
--target production .
# Run with production config
docker run -d \
--name marimo-prod \
-p 8080:8080 \
-e MARIMO_SKIP_UPDATE_CHECK= 1 \
-e DATABASE_URL_FILE=/run/secrets/db_url \
-v $( pwd ) /data:/app/data:ro \
--secret db_url \
--restart unless-stopped \
--memory 2g \
--cpus 1.5 \
marimo-app:prod
Next Steps
Deploy as App Configure app deployment options
Authentication Secure your deployment
Docker Guide Detailed Docker deployment guide
Kubernetes Advanced Kubernetes patterns