Django Deployment (Docker + gunicorn)
Production deploy: Docker images, gunicorn, env management, static files.
# CLAUDE.md — Django Deployment (Docker + gunicorn)
## Image layout
- Two-stage Dockerfile: a builder stage that installs deps + collects static, and a runtime stage that copies the artifact.
- Pin the Python image: `python:3.12-slim-bookworm`. Don't use `latest`.
- Run as a non-root user: `RUN useradd -m app && USER app`.
- One process per container. Beat, worker, and web run in separate containers.
## Dependencies
- Lock with `uv` or `pip-tools`. Commit the lock file.
- Install with `--no-cache-dir`. Cached wheels in the image bloat it.
- System deps (libpq, libjpeg) installed in the builder stage only when needed for compilation. Strip from runtime.
## gunicorn
- Workers: `2 × CPU + 1` for sync workloads. Override per-service after profiling.
- Worker class: `gthread` for I/O-bound apps with reasonable thread counts. `sync` for pure CPU. Don't reach for `gevent` without measuring.
- Bind to `0.0.0.0:8000` inside the container. Let the orchestrator handle external ports.
- `--access-logfile -` and `--error-logfile -` to send logs to stdout/stderr.
- Set `--timeout 30` and `--graceful-timeout 30`. Long-running work belongs in Celery, not the request thread.
## Static files
- `python manage.py collectstatic --noinput` runs at build time.
- Serve static via Nginx, S3, or Cloudfront — never gunicorn directly.
- Use **WhiteNoise** if you have no CDN: `STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"`.
## Environment
- 12-factor: every config value comes from env vars. No file-based secrets in the image.
- Use `django-environ` or `pydantic-settings` to parse env in one place.
- `DEBUG = False` in production. The default settings module is for prod; dev imports from it and overrides.
## Migrations
- Migrations run as a separate one-shot container before the web container starts. Never on app boot — race condition with multi-replica deploys.
- For zero-downtime deploys, schema changes are backward-compatible: add columns nullable first, deploy, then make NOT NULL in a follow-up release.
## Observability
- Logs to stdout, JSON-formatted, captured by the orchestrator.
- Health check endpoint that hits the DB and cache. Don't return 200 from a static route.
- `SENTRY_DSN` env var; initialize Sentry in `settings.py` only if the DSN is set.
## Don't
- Don't bake secrets into the image (DATABASE_URL, SECRET_KEY).
- Don't use `runserver` in production. It's not a production server.
- Don't run migrations from inside the web container at startup. Use a one-shot job.
- Don't ignore `ALLOWED_HOSTS`. An empty list with `DEBUG=False` is a 500 generator.
Other Django templates
Django + DRF Rules
Django REST Framework conventions: viewsets, serializers, permissions.
Django Models + ORM Discipline
Model design, migrations, queryset hygiene, and N+1 prevention.
Django + Celery Background Jobs
Celery task design, idempotency, retries, and Redis as broker.
Django Testing with pytest
pytest-django setup, fixtures, factories, and fast database tests.