FastAPI / Chapter 7 of 8

Deployment and performance

Deploy FastAPI behind a reverse proxy, size worker processes and database pools, and handle timeouts, health checks, and blocking work.

2 topicsUpdated
25 / 28

Production deployment: process, proxy, and database#

Run an ASGI server without reload, put HTTPS at a trusted proxy, use environment-based secrets, migrate schemas during release, and monitor errors and latency.

ShellRun commands and proxy example
# Local development only
fastapi dev app/main.py

# Production: one process per container is a common choice
fastapi run app/main.py --host 0.0.0.0 --port 8000

# Or more processes on a larger machine
fastapi run app/main.py --workers 4 --host 0.0.0.0 --port 8000

# Behind a trusted proxy, configure forwarded headers AND
# restrict which proxy IPs may supply them.
uvicorn app.main:app --host 0.0.0.0 --port 8000 \
  --proxy-headers --forwarded-allow-ips '10.0.0.10'

# For a path-prefix proxy, configure root_path as appropriate.
DockerfileMinimal container recipe
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
CMD ["fastapi", "run", "app/main.py", "--host", "0.0.0.0", "--port", "8000"]

# requirements.txt: pin compatible versions of fastapi[standard],
# sqlalchemy, psycopg, pydantic-settings, and auth libraries.
# Run database migrations as a separate release task.
ScaleOne process per container behind a load balancer, or several workers per machine.
DatabasePer-request sessions; size total connection pools across all processes.
ReleaseAlembic migration, readiness checks, graceful termination, rollback plan.
Worker math
Four app workers each with pool_size=5 can keep 20 database connections before overflow. Multiply by containers and background workers; set a deliberate pool budget for PostgreSQL.
26 / 28

Operational safeguards and performance#

Observe request latency, error rates, DB pool waits, and slow SQL. Validate at the edge and application, and keep costly I/O out of request paths when possible.

PythonSimple structured logging hook
import logging
from uuid import uuid4
from fastapi import FastAPI, Request

logger = logging.getLogger("api")
app = FastAPI()

@app.middleware("http")
async def request_id(request: Request, call_next):
    rid = request.headers.get("X-Request-ID") or str(uuid4())
    response = await call_next(request)
    response.headers["X-Request-ID"] = rid
    logger.info("request_complete", extra={
        "request_id": rid,
        "method": request.method,
        "path": request.url.path,
        "status": response.status_code,
    })
    return response
ShellAlembic release workflow
alembic init migrations                 # once
alembic revision --autogenerate -m "add books"
# Review the generated file: autogenerate is not infallible.
alembic upgrade head                    # separate release job

# Then deploy application version compatible with the schema.
# Prefer additive expand/contract changes for rolling releases.
Performance checklist
Bound pagination and upload sizes; avoid N+1 queries; cache with explicit invalidation where useful; set outbound HTTP timeouts; enforce per-process memory and request limits at the edge. Never log passwords or bearer tokens.