From 6e32955533f7e78b0e9c5321f0e398b8b2a859b7 Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Wed, 17 Jun 2026 00:28:59 -0500 Subject: [PATCH] uvicorn settings --- skills/fastapi-uv-docker/SKILL.md | 52 ++++- .../references/docker-cloud-native.md | 2 +- .../references/uvicorn-settings.md | 192 ++++++++++++++++++ 3 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 skills/fastapi-uv-docker/references/uvicorn-settings.md diff --git a/skills/fastapi-uv-docker/SKILL.md b/skills/fastapi-uv-docker/SKILL.md index 4c37db9..5efea0b 100644 --- a/skills/fastapi-uv-docker/SKILL.md +++ b/skills/fastapi-uv-docker/SKILL.md @@ -24,6 +24,7 @@ Load these references only when needed: - FastAPI patterns and app structure: [./references/fastapi-best-practices.md](./references/fastapi-best-practices.md) - uv project layout and dependency management: [./references/uv-project-layout.md](./references/uv-project-layout.md) +- uvicorn CLI settings reference: [./references/uvicorn-settings.md](./references/uvicorn-settings.md) - Docker and cloud-native patterns: [./references/docker-cloud-native.md](./references/docker-cloud-native.md) --- @@ -45,6 +46,7 @@ Before making changes, map the current state across six areas. Produce a short g Load [./references/fastapi-best-practices.md](./references/fastapi-best-practices.md) for structure rules. Load [./references/uv-project-layout.md](./references/uv-project-layout.md) for uv migration rules. +Load [./references/uvicorn-settings.md](./references/uvicorn-settings.md) for uvicorn CLI reference. Completion check: You can name every gap before touching any file. @@ -209,28 +211,58 @@ Completion check: `uv run uvicorn my_app.main:app --reload` starts with no impor ### Step 4: uvicorn Production Configuration -**Never** configure uvicorn inside application code. Pass all settings via CLI or environment. +Load [./references/uvicorn-settings.md](./references/uvicorn-settings.md) for the full settings reference. + +**Never** configure uvicorn inside application code. Pass all settings via CLI or environment variables (`UVICORN_*` prefix). ```bash -# Development -uv run uvicorn my_app.main:app --reload --host 127.0.0.1 --port 8000 +# Development — reload only; never in production +uv run uvicorn my_app.main:app \ + --reload \ + --host 127.0.0.1 \ + --port 8000 \ + --log-level debug -# Production (single process — let orchestrator handle replication) +# Production — single process (orchestrator handles replication) uv run uvicorn my_app.main:app \ --host 0.0.0.0 \ --port 8000 \ --workers 1 \ - --loop uvloop \ - --http h11 \ + --loop auto \ + --http auto \ --log-level info \ - --access-log \ --proxy-headers \ - --forwarded-allow-ips '*' + --forwarded-allow-ips '*' \ + --timeout-graceful-shutdown 30 ``` -**When to use `--workers > 1`:** Only for single-server Docker Compose deployments where you cannot replicate at the orchestrator level. For Kubernetes / cloud run: always `--workers 1` and scale via replicas. +**Key flags for production:** -**`--proxy-headers`** is required whenever the container sits behind a reverse proxy (nginx, traefik, cloud load balancer) — it makes FastAPI trust `X-Forwarded-For` and `X-Forwarded-Proto`. +| Flag | Value | Reason | +|------|-------|--------| +| `--host 0.0.0.0` | Required in containers | Bind to all interfaces, not just loopback | +| `--workers 1` | Kubernetes/Cloud Run | Orchestrator replicates containers | +| `--loop auto` | Default | Uses `uvloop` when available (install `uvicorn[standard]`) | +| `--http auto` | Default | Uses `httptools` when available | +| `--proxy-headers` | Behind any proxy | Trusts `X-Forwarded-For`, `X-Forwarded-Proto` | +| `--forwarded-allow-ips '*'` | Container/K8s | Trusts proxy headers from all IPs (safe when inside a trusted network) | +| `--timeout-graceful-shutdown 30` | Prod | Seconds to wait before force-closing requests on shutdown | +| `--no-access-log` | High-traffic prod | Disable per-request logs if using structured app-level logging | + +**Environment variable equivalents** (useful in `docker-compose.yml` / K8s manifests): + +```bash +UVICORN_HOST=0.0.0.0 +UVICORN_PORT=8000 +UVICORN_WORKERS=1 +UVICORN_LOG_LEVEL=info +UVICORN_PROXY_HEADERS=true +UVICORN_FORWARDED_ALLOW_IPS=* +``` + +**`--reload` and `--workers` are mutually exclusive** — never combine them. + +**When to use `--workers > 1`:** Only for Docker Compose on a single host where orchestrator-level replication is not available. For Kubernetes / Cloud Run / Fargate: always `--workers 1` and scale via replicas — this gives predictable per-container memory and cleaner crash isolation. Completion check: `curl http://localhost:8000/healthz` returns `{"status":"ok"}`. diff --git a/skills/fastapi-uv-docker/references/docker-cloud-native.md b/skills/fastapi-uv-docker/references/docker-cloud-native.md index 1d0c052..070faaf 100644 --- a/skills/fastapi-uv-docker/references/docker-cloud-native.md +++ b/skills/fastapi-uv-docker/references/docker-cloud-native.md @@ -1,6 +1,6 @@ # Docker and Cloud-Native Patterns -Source: https://docs.docker.com/build/building/best-practices/ | https://docs.astral.sh/uv/guides/integration/docker/ | https://fastapi.tiangolo.com/deployment/docker/ +Source: https://docs.docker.com/build/building/best-practices/ | https://docs.astral.sh/uv/guides/integration/docker/ | https://fastapi.tiangolo.com/deployment/docker/ | https://uvicorn.dev/deployment/ --- diff --git a/skills/fastapi-uv-docker/references/uvicorn-settings.md b/skills/fastapi-uv-docker/references/uvicorn-settings.md new file mode 100644 index 0000000..b2fdf55 --- /dev/null +++ b/skills/fastapi-uv-docker/references/uvicorn-settings.md @@ -0,0 +1,192 @@ +# uvicorn Settings Reference + +Source: https://uvicorn.dev/settings/ | https://uvicorn.dev/deployment/ + +--- + +## Configuration Methods + +Three equivalent approaches (CLI takes precedence over env vars): + +```bash +# 1. CLI flags +uvicorn main:app --host 0.0.0.0 --port 8000 + +# 2. UVICORN_* environment variables +export UVICORN_HOST=0.0.0.0 +export UVICORN_PORT=8000 +uvicorn main:app + +# 3. Programmatic (dev/test only) +uvicorn.run("main:app", host="0.0.0.0", port=8000) +``` + +**Note:** `UVICORN_*` env vars cannot be used from within an `--env-file`. The `--env-file` flag is for the ASGI *application's* config, not uvicorn's own config. + +--- + +## All Settings by Category + +### Socket Binding + +| Flag | Default | Notes | +|------|---------|-------| +| `--host ` | `127.0.0.1` | Use `0.0.0.0` in containers to bind all interfaces | +| `--port ` | `8000` | Use `0` to auto-pick an available port | +| `--uds ` | — | UNIX domain socket path (use behind Nginx) | +| `--fd ` | — | Inherit socket from file descriptor (use with Supervisor) | + +### Production + +| Flag | Default | Notes | +|------|---------|-------| +| `--workers ` | `1` (or `$WEB_CONCURRENCY`) | **Mutually exclusive with `--reload`** | +| `--env-file ` | — | Env file for the *application* (not uvicorn itself) | +| `--timeout-worker-healthcheck ` | `5` | Seconds before killing a stuck worker | + +### Logging + +| Flag | Default | Notes | +|------|---------|-------| +| `--log-level ` | `info` | `critical`, `error`, `warning`, `info`, `debug`, `trace` | +| `--log-config ` | — | `.json` or `.yaml` for `dictConfig()`; other formats use `fileConfig()` | +| `--no-access-log` | — | Disable access log without changing log level | +| `--use-colors / --no-use-colors` | auto | Force color on/off in log output | + +### Implementation + +| Flag | Default | Notes | +|------|---------|-------| +| `--loop ` | `auto` | `auto`, `asyncio`, `uvloop`. `uvloop` requires `uvicorn[standard]` | +| `--http ` | `auto` | `auto`, `h11`, `httptools`. `httptools` requires `uvicorn[standard]` | +| `--ws ` | `auto` | `auto`, `none`, `websockets`, `websockets-sansio`, `wsproto` | +| `--lifespan ` | `auto` | `auto`, `on`, `off` | +| `--ws-max-size ` | `16777216` | WebSocket max message size in bytes (16 MB) | +| `--ws-ping-interval ` | `20.0` | WebSocket ping interval in seconds | +| `--ws-ping-timeout ` | `20.0` | WebSocket ping timeout in seconds | + +### HTTP / Proxy Headers + +| Flag | Default | Notes | +|------|---------|-------| +| `--proxy-headers` | enabled | Trust `X-Forwarded-For`, `X-Forwarded-Proto` from trusted IPs | +| `--no-proxy-headers` | — | Disable proxy header trust entirely | +| `--forwarded-allow-ips ` | `127.0.0.1` | Comma-separated IPs/networks/literals to trust. Use `'*'` to trust all (safe in containers behind a trusted LB). **Security risk if exposed directly to internet.** | +| `--root-path ` | `""` | ASGI `root_path` for apps mounted below a URL prefix | +| `--server-header / --no-server-header` | enabled | Include/suppress `Server` response header | +| `--date-header / --no-date-header` | enabled | Include/suppress `Date` response header | +| `--header ` | — | Add custom default response headers (repeatable) | + +### Resource Limits + +| Flag | Default | Notes | +|------|---------|-------| +| `--limit-concurrency ` | — | Max concurrent connections/tasks; returns HTTP 503 above this | +| `--limit-max-requests ` | — | Restart worker after N requests (limits memory leak accumulation) | +| `--limit-max-requests-jitter ` | `0` | Random jitter added to `--limit-max-requests` to stagger worker restarts | +| `--backlog ` | `2048` | Max queued connections under high load | + +### Timeouts + +| Flag | Default | Notes | +|------|---------|-------| +| `--timeout-keep-alive ` | `5` | Close keep-alive connections after N seconds of inactivity | +| `--timeout-graceful-shutdown ` | — | Seconds to wait for in-flight requests to complete on SIGTERM before force-closing | + +### Development + +| Flag | Default | Notes | +|------|---------|-------| +| `--reload` | `False` | Auto-reload on file changes. **Never use in production.** Mutually exclusive with `--workers`. | +| `--reload-dir ` | `.` | Directory to watch for changes (repeatable) | +| `--reload-delay ` | `0.25` | Seconds between reload checks | +| `--reload-include ` | `*.py` | Patterns to include in watch (requires `watchfiles`) | +| `--reload-exclude ` | `.*, .py[cod], ...` | Patterns to exclude from watch (requires `watchfiles`) | + +### Application + +| Flag | Default | Notes | +|------|---------|-------| +| `--factory` | — | Treat `APP` as a `() -> ASGI app` callable (app factory pattern) | +| `--app-dir ` | `.` | Add to `PYTHONPATH` when resolving `APP` | +| `--reset-contextvars` | `False` | Run each request in a fresh `contextvars.Context` (asyncio only; workaround for a CPython context-leak bug) | + +--- + +## Recommended Production CMD + +```bash +uvicorn my_app.main:app \ + --host 0.0.0.0 \ + --port 8000 \ + --workers 1 \ + --loop auto \ + --http auto \ + --log-level info \ + --proxy-headers \ + --forwarded-allow-ips '*' \ + --timeout-graceful-shutdown 30 +``` + +In a Dockerfile (exec form): + +```dockerfile +CMD ["uvicorn", "my_app.main:app", + "--host", "0.0.0.0", + "--port", "8000", + "--workers", "1", + "--proxy-headers", + "--forwarded-allow-ips", "*", + "--timeout-graceful-shutdown", "30"] +``` + +--- + +## Process Manager Options + +### Built-in multi-worker (Docker Compose / single host) + +```bash +uvicorn my_app.main:app --workers 4 +``` + +The built-in manager spawns workers, monitors their health, and auto-restarts crashed workers. Signal support: +- `SIGHUP` — rolling graceful restart (deploy new code without dropping requests) +- `SIGTTIN` — add one worker +- `SIGTTOU` — remove one worker + +### Behind Nginx (UNIX socket) + +```bash +uvicorn my_app.main:app --uds /tmp/uvicorn.sock --proxy-headers +``` + +Nginx config headers to set: +```nginx +proxy_set_header Host $http_host; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; +``` + +--- + +## `uvicorn[standard]` vs bare `uvicorn` + +| Package | Extras included | +|---------|----------------| +| `uvicorn` | Pure Python h11 HTTP, asyncio event loop | +| `uvicorn[standard]` | `uvloop` (faster event loop), `httptools` (faster HTTP parser), `watchfiles` (better reload), `websockets`, `PyYAML` (for `--log-config`) | + +Use `uvicorn[standard]` in all environments. The `[standard]` extras are also included when you install `fastapi[standard]`. + +--- + +## Anti-patterns + +| Anti-pattern | Fix | +|---|---| +| `--reload` in Dockerfile CMD | Remove it — `--reload` is dev-only | +| `--loop uvloop` explicitly | Use `--loop auto` — it selects uvloop automatically when available | +| `--http h11` explicitly in prod | Use `--http auto` — it selects httptools when available | +| `uvicorn.run()` at module level (no `if __name__ == '__main__':`) | Breaks multiprocessing workers; always guard it | +| Shell form `CMD uvicorn ...` | Exec form `CMD ["uvicorn", ...]` — required for SIGTERM to reach uvicorn |