Uvicorn Production Config

Difficulty: Advanced

Running FastAPI in production requires careful configuration of the ASGI server. Uvicorn is the recommended server, but production deployment differs significantly from development. You need multiple worker processes, proper logging, SSL/TLS configuration, and process management.

In development, you run uvicorn main:app --reload with a single worker process. In production, you remove --reload (it adds overhead and watches files unnecessarily) and add multiple workers with --workers N. The number of workers should be (2 * CPU cores) + 1 as a starting point. Each worker is an independent process that handles requests concurrently.

Gunicorn with UvicornWorker is the recommended production setup. Gunicorn provides robust process management: automatic worker restarts on crashes, graceful shutdown on SIGTERM, pre-forking for efficient memory usage, and signal handling. The command is: gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4.

SSL/TLS can be terminated at Uvicorn or, more commonly, at a reverse proxy (Nginx, Traefik, or a cloud load balancer). If terminating at Uvicorn, use --ssl-certfile and --ssl-keyfile. In most production setups, a reverse proxy handles SSL and forwards plain HTTP to Uvicorn.

Logging configuration is important for monitoring and debugging. Uvicorn uses Python's logging module. In production, set the log level to 'info' or 'warning' (not 'debug'). Use structured logging (JSON format) for easy parsing by log aggregation tools like ELK or Datadog. Configure access logs to include request method, path, status code, and timing.

Performance tuning involves several factors: number of workers (CPU-bound), backlog size (connection queue), keep-alive timeout (connection reuse), and limit-concurrency (maximum simultaneous connections per worker). Monitor your application's metrics to find optimal values.

Code examples

Production Uvicorn Configuration

# Development
uvicorn main:app --reload

# Production (single process)
uvicorn main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --log-level info \
    --access-log \
    --no-server-header

# Production with Gunicorn (recommended)
gunicorn main:app \
    --bind 0.0.0.0:8000 \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker \
    --timeout 120 \
    --keep-alive 5 \
    --access-logfile - \
    --error-logfile - \
    --graceful-timeout 30

# Workers formula: (2 * CPU_CORES) + 1
# 2 cores  -> 5 workers
# 4 cores  -> 9 workers
# 8 cores  -> 17 workers

Production removes --reload and adds multiple workers. Gunicorn with UvicornWorker is preferred for process management. --access-logfile - sends logs to stdout (standard for containers). --graceful-timeout allows in-flight requests to complete during shutdown.

Programmatic Configuration

# config.py
import os
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # Server
    host: str = "0.0.0.0"
    port: int = 8000
    workers: int = 4
    reload: bool = False

    # Database
    database_url: str = "sqlite:///./app.db"

    # Auth
    secret_key: str = "change-me"
    access_token_expire_minutes: int = 30

    # CORS
    allowed_origins: list[str] = ["http://localhost:3000"]

    class Config:
        env_file = ".env"

settings = Settings()

# main.py
import uvicorn
from fastapi import FastAPI
from config import settings

app = FastAPI()

if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host=settings.host,
        port=settings.port,
        workers=settings.workers,
        reload=settings.reload,
        log_level="info",
    )

# .env file:
# DATABASE_URL=postgresql://user:pass@db:5432/myapp
# SECRET_KEY=super-secret-production-key
# WORKERS=8
# ALLOWED_ORIGINS=["https://myapp.com"]

pydantic-settings provides type-safe configuration from environment variables and .env files. Settings are centralized and accessible throughout the app. Running with python main.py uses the programmatic configuration.

Nginx Reverse Proxy

# nginx.conf
upstream fastapi_backend {
    server api:8000;
}

server {
    listen 80;
    server_name myapp.com;

    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name myapp.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

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

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Serve static files directly
    location /static/ {
        alias /app/static/;
        expires 30d;
    }
}

Nginx sits in front of Uvicorn handling SSL termination, HTTP/2, security headers, and static file serving. It forwards API requests to the Uvicorn backend. X-Forwarded headers tell FastAPI the real client IP and protocol.

Key points

Concepts covered

Uvicorn, Workers, Gunicorn, SSL, Logging, Performance