A Cookiecutter template for a production-minded Django 6 API service with Django Ninja, typed settings, reproducible uv-managed dependencies, Docker Compose deployment defaults, and CI gates for the baked project.
/api/v1/business API versioning plus unversioned health and readiness probes- Optional cache-backed throttling for public API routes
- 100% pytest coverage gate with factories and Schemathesis property-based contract tests
- actionlint, gitleaks, gitlint, markdownlint, Ruff, shellcheck, Ty, uv-audit, yamlfmt, yamllint, and other pre-commit checks
- Custom user model from the initial bake
- Dependabot for Compose, Docker, GitHub Actions, pre-commit hooks, and uv
- Dockerfile and Docker Compose definitions for development and production
django-split-settingswithci,dev, andprodoverlays- OpenAPI schema export command with committed
docs/openapi/schemas and a CI drift gate, ready for client generation - Optional Celery worker and beat services, chosen at bake time
- Optional CORS support for explicit browser origins
- Optional Django native Content Security Policy for browser-rendered surfaces
- Optional email stack: Resend API, SMTP, or no production email provider
- Optional example
notesresource demonstrating the model-to-tests vertical slice - Optional S3-compatible media storage with a provider-neutral recovery contract
- Optional Sentry integration
- Optional Traefik ingress with Let's Encrypt or operator-provided TLS
pg_dumpbackup script and restore runbook for the bundled Postgres- PostgreSQL and Redis as bundled Compose services or external production services
- Standardized production deploy path with docker-rollout when Traefik is enabled
- uv-managed Python 3.14 dependencies
- Vendored
.agents/skills andAGENTS.mdguidance
- API versioning is explicit: internal probes stay under
/api/, while business endpoints start at/api/v1/. - Celery uses Redis and stores results only for tasks that opt in, keeping the default result backend quiet.
- Custom user model ships from day one because changing it later is one of Django's expensive irreversible decisions.
- Django Ninja is the API layer because it gives typed handlers and OpenAPI output with little framework overhead.
django-split-settingskeeps settings split by concern, with small environment overlays forci,dev, andprod.- Docker Compose is the deployment contract; production can bundle Traefik, PostgreSQL, and Redis or point at external backing services.
- Liveness (
/api/health) and readiness (/api/ready) are separate so container restarts and load-balancer routing can make different decisions. - PostgreSQL is the database target everywhere, including tests: the suite runs against a real Postgres so migrations and engine semantics are exercised.
- Production Sentry is boot-required when enabled, so broken observability fails before traffic reaches the app.
- Metrics are a project-level add-on, not a template knob: Sentry already
covers error rates and sampled latency, and a scraped
/metricsendpoint would add an unauthenticated attack surface plus a full knob lifecycle to maintain, which is not worth it for single-operator deployments. The generated README documents the django-prometheus and OpenTelemetry recipes. - CORS is opt-in and requires explicit allowed browser origins; throttling is deliberately not enabled by default.
- The
src/layout keeps import paths honest and avoids accidentally importing from the repository root. - A bounded uv compatibility range supports patch updates, while exact uv artifact pins and generated lockfiles keep fresh bakes reproducible.
- Tests measure all of
src/and fail below 100% coverage.
- Docker Compose >= 5.3.0 for
pre_startlifecycle hooks - Python 3.14
- uv >=0.11.29,<0.12.0
uvx --from=cookiecutter==2.7.1 cookiecutter gh:stefanofusai/django-api-template| Name | Default | Description |
|---|---|---|
project_name |
My Project |
Human-readable project name. |
project_slug |
my-project |
Repository and package distribution name, derived from project_name unless set explicitly. |
description |
A Django Ninja API service. |
Generated README and package description. Must not contain ", \, or newlines. |
author_name |
John Doe |
Package author and maintainer name. Must not contain ", \, or newlines. |
author_email |
john.doe@example.com |
Package author and maintainer email. |
github_username |
johndoe |
GitHub owner (user or org) used for Dependabot assignees and the GHCR image path. |
domain_name |
example.com |
Deployment domain pre-filled into ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS, and TRAEFIK_DOMAIN. |
api_auth |
session |
Authentication used by the example notes API: session (Django session auth with CSRF) or jwt (JWT access and refresh tokens via django-ninja-jwt); only takes effect when use_example_api=yes. |
api_throttling |
none |
Public API throttling: none disables throttling, basic enables cache-backed fixed-window throttling. |
behind_proxy |
yes |
Trust an upstream TLS-terminating proxy's X-Forwarded-Proto; use no only for plain-HTTP private-network production. |
email_provider |
resend |
Production email provider: resend, smtp, or none. |
postgres |
compose |
Run production Postgres as a bundled Compose service, or point DATABASE_URL at an external/managed Postgres-compatible database. |
redis |
compose |
Run production Redis as a bundled Compose service, or point CACHE_URL and CELERY_BROKER_URL at external Redis-protocol providers. |
traefik_tls |
letsencrypt |
Use external to serve an operator-provided PEM pair instead of running ACME; ignored when use_traefik=no. |
use_celery |
worker+beat |
Celery services to include: worker+beat, worker, or none. |
use_cors |
no |
Enable explicit browser origins with django-cors-headers and CORS_ALLOWED_ORIGINS. |
use_csp |
no |
Enable Django native CSP headers: script-src blocks inline scripts (self-hosted Swagger UI, unsafe-eval retained for the admin theme); inline styles are still allowed. |
use_example_api |
no |
Include the example notes model, router, and tests demonstrating the full API pattern. |
use_s3_media |
yes |
Store production media on S3-compatible object storage. |
use_sentry |
yes |
Include the production Sentry integration. |
use_traefik |
yes |
Include the bundled Traefik reverse proxy. |
project_slug must start with a lowercase letter, contain only lowercase
letters, digits, and single hyphen separators, and be 50 characters or fewer.
The feature-knob defaults reproduce the historical full-stack output.
domain_name must be a bare lowercase hostname with at least one dot.
After baking, copy .env.example to .env:
cp .env.example .envBefore production deploy:
- Replace
SECRET_KEYwith a securely generated value. - Set
CORS_ALLOWED_ORIGINSwhenuse_cors=yes. - Set
AWS_STORAGE_BUCKET_NAMEwhenuse_s3_media=yes. - Set
RESEND_API_KEYwhenemail_provider=resend, orEMAIL_HOSTwhenemail_provider=smtp. - Set
SENTRY_DSNwhenuse_sentry=yes. - Set
TRAEFIK_ACME_EMAILwhenuse_traefik=yesandtraefik_tls=letsencrypt. - Review
DATABASE_URL,CACHE_URL, andCELERY_BROKER_URLwhen baking with external Postgres or Redis. - Bake with the real
domain_namesoALLOWED_HOSTS,CSRF_TRUSTED_ORIGINS, andTRAEFIK_DOMAINare pre-filled; production boot refusesexample.cominALLOWED_HOSTS.
See the generated .env.example comments and the generated README's
Production section for the full deployment checklist.
The post-generation hook initializes a Git repository and runs uv lock with
exactly uv 0.11.29 when uv is available. uv.lock is generated in the baked
project instead of stored in the template because it embeds the project name.
After generation:
uv sync --locked
cp .env.example .env
uv run pre-commit install --install-hooks
mkdir -p docs/openapi
uv run python manage.py export_openapi_schema --api=internal --output=docs/openapi/openapi-internal.json
uv run python manage.py export_openapi_schema --api=v1 --output=docs/openapi/openapi-v1.json
git add -A
git commit -m "feat: initial project scaffold"Run the complete bake verification with one locked command:
uv run --locked python scripts/verify_bake.pyFreshly baked projects are expected to pass:
docker compose -f .docker/compose/dev.yaml --env-file=.env up -d --wait postgres
uv run pytest
uv run pre-commit run --all-files
docker compose -f .docker/compose/dev.yaml --env-file=.env up -d --build --wait
curl -fsS http://localhost:8000/api/ready
docker compose -f .docker/compose/dev.yaml --env-file=.env down -vMIT.