Skip to content

Deployment¶

This guide covers everything you need to deploy a Cello application in production: server configuration, worker processes, Docker packaging, TLS/HTTPS, reverse proxy setup, health checks, and monitoring.


Production Settings¶

Command-Line Options¶

python app.py \
  --host 0.0.0.0 \
  --port 8000 \
  --env production \
  --workers 4 \
  --no-logs
Flag Default Description
--host 127.0.0.1 Bind address. Use 0.0.0.0 in containers.
--port 8000 Listening port
--env development Set to production to disable debug mode
--workers CPU count Number of worker threads
--debug Off in prod Enable verbose error pages
--no-logs Off Disable request logging

Programmatic Configuration¶

app.run(
    host="0.0.0.0",
    port=8000,
    env="production",
    workers=8,
    logs=False,
)

Workers Configuration¶

Cello uses Tokio worker threads inside Rust. The --workers flag controls how many OS threads handle requests concurrently.

Rule of thumb: Set workers to the number of CPU cores.

python app.py --workers $(nproc)

For I/O-bound workloads (database queries, external API calls), you can increase workers to 2x the CPU count.


Environment Variables¶

Use environment variables for configuration that changes between environments.

export CELLO_ENV=production
export DATABASE_URL=postgresql://user:pass@db:5432/app
export JWT_SECRET=$(python -c "import secrets; print(secrets.token_urlsafe(64))")
export WORKERS=4

Read them in your application:

import os

app.run(
    host=os.environ.get("HOST", "0.0.0.0"),
    port=int(os.environ.get("PORT", "8000")),
    env=os.environ.get("CELLO_ENV", "production"),
    workers=int(os.environ.get("WORKERS", "4")),
)

TLS / HTTPS¶

Cello supports TLS natively through Rustls (no OpenSSL dependency).

from cello import App, TlsConfig

app = App()

tls = TlsConfig(
    cert_path="/etc/ssl/certs/server.crt",
    key_path="/etc/ssl/private/server.key",
)

app.run(host="0.0.0.0", port=443, tls=tls)

For Let's Encrypt certificates, point cert_path and key_path to the fullchain and private key files.


Docker Deployment¶

Dockerfile¶

# Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY . .

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health/live')"

CMD ["python", "app.py", "--host", "0.0.0.0", "--env", "production", "--workers", "4"]

docker-compose.yml¶

version: "3.8"
services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      CELLO_ENV: production
      DATABASE_URL: postgresql://user:pass@db:5432/app
      JWT_SECRET: change-me-in-production
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Reverse Proxy (nginx)¶

In production, place nginx in front of Cello for TLS termination, static files, and load balancing.

nginx.conf¶

upstream cello {
    server 127.0.0.1:8000;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;

    location / {
        proxy_pass http://cello;
        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;
    }

    location /ws/ {
        proxy_pass http://cello;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Note

The /ws/ location block is required for WebSocket connections. Without the Upgrade headers, WebSocket handshakes fail.


Health Checks¶

Enable health check endpoints for orchestrators like Kubernetes and Docker.

from cello import HealthCheckConfig

app.enable_health_checks(HealthCheckConfig(
    base_path="/health",
    include_system_info=True,
))

This registers:

Endpoint Purpose
GET /health/live Liveness probe -- is the process alive?
GET /health/ready Readiness probe -- is it ready for traffic?
GET /health/startup Startup probe -- has initialization completed?
GET /health Full health report with system info

Monitoring¶

Prometheus Metrics¶

app.enable_prometheus(endpoint="/metrics")

Scrape /metrics from your Prometheus server. The endpoint exposes request count, latency histograms, and error rates.

OpenTelemetry¶

For distributed tracing across microservices:

from cello import OpenTelemetryConfig

app.enable_telemetry(OpenTelemetryConfig(
    service_name="my-service",
    otlp_endpoint="http://collector:4317",
))

See the OpenTelemetry guide for details.


Pre-Deployment Checklist¶

  • Set --env production
  • Configure --workers based on CPU count
  • Enable TLS (directly or via reverse proxy)
  • Set JWT_SECRET and other secrets via environment variables
  • Enable health checks
  • Enable Prometheus metrics
  • Enable rate limiting on public endpoints
  • Enable security headers
  • Test with production-like load before going live
  • Set up log aggregation (stdout logs are collected by Docker/K8s)

Next Steps¶