Checklist
#django

Django Deployment Checklist for Production

A Django app that works locally is not automatically safe to run in production. Most deployment failures come from a small set of issues: wrong settings, missing secrets, unsafe...

Problem statement

A Django app that works locally is not automatically safe to run in production. Most deployment failures come from a small set of issues: wrong settings, missing secrets, unsafe TLS or proxy handling, unreviewed migrations, broken static files, or no rollback plan.

This Django deployment checklist for production is a practical pre-release and post-release runbook. It helps you verify that your app is ready to go live on a Linux server, VM, or container-based stack using common components like Gunicorn, Uvicorn, Nginx, Caddy, PostgreSQL, and Redis.

What this Django deployment checklist covers

This checklist focuses on production readiness:

  • Django settings and environment separation
  • secrets and credentials
  • database readiness and backups
  • static and media file handling
  • app server, reverse proxy, and TLS checks
  • logging, monitoring, and background workers
  • deployment order, rollback, and post-deploy smoke tests

What this checklist does not replace

It does not replace:

  • a full infrastructure design
  • app-specific load testing
  • a disaster recovery plan for your entire platform
  • platform-specific docs from your host or managed database provider

Quick answer

Before go-live, verify these minimum items:

  1. DEBUG = False
  2. ALLOWED_HOSTS is correct
  3. SECRET_KEY and database credentials come from environment variables or a secret manager
  4. PostgreSQL is reachable and backups are enabled
  5. python manage.py check --deploy passes
  6. migrations are reviewed before applying
  7. collectstatic succeeds
  8. Gunicorn or Uvicorn starts cleanly
  9. Nginx or Caddy serves the app over HTTPS
  10. logs, health checks, and rollback steps are ready

The highest-risk items to verify first

If time is limited, check these first:

  • secrets are not hardcoded or committed
  • database migrations are safe for the current release
  • TLS and proxy headers are correct
  • static files and media storage are configured correctly
  • rollback is possible if the release fails

Django deployment checklist for production

1. Confirm environment separation

Production settings must be separate from local development behavior.

Minimum checks:

  • DEBUG = False
  • ALLOWED_HOSTS includes the real domain names
  • production settings are not mixed with dev defaults
  • secrets are not stored in Git
  • required environment variables must exist at startup

Example settings:

import os

DEBUG = False

ALLOWED_HOSTS = ["example.com", "www.example.com"]

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]

Verification:

env | grep DJANGO
python manage.py check --deploy

Rollback note: if the app fails at startup after a settings change, revert the settings file or restore the previous environment file before restarting the process.

2. Verify secret and credential handling

Do not hardcode production credentials in Django settings, Compose files committed to Git, or service files.

Check that these come from environment variables or a secret manager:

  • SECRET_KEY
  • database username and password
  • email credentials
  • third-party API keys
  • Redis URLs if used

Environment file structure example:

DJANGO_SECRET_KEY=replace-with-a-real-secret
DATABASE_URL=postgresql://appuser:strongpassword@127.0.0.1:5432/appdb
REDIS_URL=redis://127.0.0.1:6379/0
DJANGO_ALLOWED_HOSTS=example.com,www.example.com

Also confirm a basic rotation process exists. Even if it is manual, document where secrets live and how they are updated.

3. Validate database readiness

Before deployment:

  • production PostgreSQL database exists
  • connectivity works
  • the application user has the right permissions
  • backups are enabled
  • restore has been tested at least once
  • migration impact has been reviewed

Useful commands:

python manage.py dbshell
python manage.py showmigrations

Apply migrations only when you understand what they will do:

python manage.py migrate

Rollback note: database rollbacks are often harder than code rollbacks. If a migration is destructive, locks large tables, or is not backward-compatible with older code, plan that release separately.

4. Review static and media file handling

Static files and user uploads are different problems.

Check static files:

python manage.py collectstatic --noinput

Verify:

  • static files are served by Nginx, Caddy, or object storage
  • hashed filenames or cache-safe asset versioning is in place where relevant
  • browser requests for CSS and JS return 200

For media:

  • uploads use persistent storage
  • the storage path survives container restarts or server redeploys
  • file permissions are correct

If user uploads are stored on local disk, confirm the mount or directory is backed up and not tied to disposable releases.

5. Check application server configuration

Your app server should start cleanly before you route live traffic to it.

Gunicorn test start:

gunicorn config.wsgi:application --bind 127.0.0.1:8000

systemd checks:

sudo systemctl status gunicorn
sudo systemctl restart gunicorn
sudo journalctl -u gunicorn -n 100 --no-pager

Example systemd excerpt:

[Service]
User=www-data
Group=www-data
WorkingDirectory=/srv/myapp/current
EnvironmentFile=/srv/myapp/shared/.env
ExecStart=/srv/myapp/venv/bin/gunicorn config.wsgi:application --bind 127.0.0.1:8000 --workers 3 --timeout 60
Restart=always

Verify:

  • worker count is chosen intentionally
  • timeout is not left at an unsuitable default
  • restart policy exists
  • a health endpoint such as /healthz exists if possible

6. Check reverse proxy and TLS

Your reverse proxy must route requests correctly and preserve HTTPS information for Django.

Nginx checks:

sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/
curl https://example.com/healthz

Example Nginx server block:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location /static/ {
        alias /srv/myapp/shared/static/;
    }

    location /media/ {
        alias /srv/myapp/shared/media/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

In Django, review:

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True

Only set SECURE_PROXY_SSL_HEADER if your reverse proxy is trusted to set and sanitize X-Forwarded-Proto. Do not trust client-supplied forwarding headers directly.

Also verify secure behavior from the browser side:

  • HTTP redirects to HTTPS
  • secure cookies are set over HTTPS
  • login and CSRF-protected forms work behind the proxy
  • redirect loops do not occur

If your deployment depends on forwarded host headers, review whether USE_X_FORWARDED_HOST = True is actually needed. Do not enable it unless your proxy setup requires it.

7. Harden Django security settings

At minimum, review these settings:

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = False

Enable HSTS only after HTTPS is working correctly for the domain, and be careful with SECURE_HSTS_INCLUDE_SUBDOMAINS if not all subdomains are HTTPS-ready.

Also check:

CSRF_TRUSTED_ORIGINS = ["https://example.com", "https://www.example.com"]

And review:

  • admin exposure is intentional
  • unnecessary debug tools are removed
  • CSRF and session settings match your real HTTPS origins

Run:

python manage.py check --deploy

8. Run pre-deployment verification

Before release, run a short verification block:

python manage.py check --deploy
python manage.py showmigrations
python manage.py collectstatic --noinput

Confirm:

  • dependency installation succeeds
  • required environment variables exist
  • external services are reachable
  • release artifact or image tag is correct

For Docker:

docker compose ps
docker compose logs web --tail=100
docker compose exec web python manage.py check --deploy

9. Plan the deployment sequence

A simple safe sequence is:

  1. pull code or deploy image
  2. install dependencies if needed
  3. run migrations
  4. run collectstatic
  5. restart or reload app process
  6. confirm web health
  7. confirm workers and scheduled jobs

Non-Docker example:

python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn
sudo systemctl status gunicorn
curl https://example.com/healthz

Docker example:

docker compose pull
docker compose up -d db redis
docker compose run --rm web python manage.py migrate
docker compose run --rm web python manage.py collectstatic --noinput
docker compose up -d web
docker compose logs web --tail=100

Do not assume docker compose up -d before migrations is safe. Make the order explicit so new app containers do not start against an incompatible schema.

If your deployment process depends on migrations completing before app restart, keep that order explicit and documented.

10. Confirm observability and logging

At minimum, make sure you can access:

  • Django application logs
  • Gunicorn or Uvicorn logs
  • Nginx or Caddy logs
  • error tracking if configured
  • uptime or health monitoring

Verification:

sudo journalctl -u gunicorn -n 100 --no-pager
docker compose logs web --tail=100

If you cannot inspect logs quickly during a release, production debugging will be slow.

11. Verify background jobs and async components

If you use Celery, Redis, Channels, or scheduled jobs, include them in the release checklist.

Verify:

  • Celery workers start
  • Celery beat is enabled if used
  • Redis is reachable
  • retry behavior and queue backlog are understood

A deployment is not complete if the web app is healthy but background jobs are dead.

12. Prepare rollback and recovery steps

Before deploying, define:

  • how to restore the previous release
  • how to redeploy a previous image tag or release directory
  • whether the new code is compatible with current database schema
  • who decides rollback

Important: code rollback may not fully undo a migration. If the migration changed schema in a non-backward-compatible way, rollback may require a separate database plan.

13. Run post-deployment checks

After release, smoke test the app:

  • homepage loads over HTTPS
  • login works
  • admin login works if enabled
  • a database write succeeds
  • CSS and JS load correctly
  • one critical user flow completes
  • background workers are processing jobs

Useful checks:

curl -I https://example.com/
curl https://example.com/healthz

Example production verification workflow

Manual checklist for a non-Docker server

  1. SSH into the server
  2. confirm .env or secret source is updated
  3. activate the virtualenv
  4. run check --deploy
  5. review migrations
  6. run migrate
  7. run collectstatic
  8. restart Gunicorn
  9. test Nginx config and reload if changed
  10. smoke test the live site
  11. watch logs for a few minutes

Manual checklist for a Docker-based deployment

  1. confirm image tag or release commit
  2. confirm environment variables for containers
  3. docker compose pull
  4. start only required dependencies first if needed
  5. run migrate in a one-off web container or controlled release step
  6. run collectstatic if your setup requires it
  7. start or update the web container
  8. inspect container logs
  9. test HTTPS and health endpoints
  10. verify worker containers separately

Where teams usually miss production checks

Common misses:

  • ALLOWED_HOSTS not updated
  • no persistent media storage
  • migrations applied without review
  • TLS works externally but proxy headers are wrong internally
  • Celery not restarted with the release
  • no tested rollback path

When to convert this process into scripts or templates

Once your release steps become repetitive, script the checks in the same order every time. Good first targets are environment validation, check --deploy, migration execution, collectstatic, service restarts, and smoke tests. Reusable templates also help for Django settings, systemd units, reverse proxy configs, and CI/CD workflows.

Explanation

Why this checklist is ordered by deployment risk

The highest-risk items come first: settings, secrets, database state, and TLS. If any of those are wrong, the deployment can fail completely or expose data. Static assets, workers, and monitoring matter too, but they are easier to diagnose after the core release path is safe.

Why migrations, secrets, and TLS should be verified before release

Migrations can lock tables or make old code incompatible. Secrets can stop the app from starting or expose production systems. TLS and proxy header mistakes can break redirects, secure cookies, CSRF validation, and login behavior.

Why rollback must be planned before the deployment starts

Rollback is a release requirement, not a cleanup step. If you only think about recovery after a failed deployment, you are already in incident mode.

Edge cases and notes

Deploying behind a load balancer or platform proxy

If Django is behind Nginx, Caddy, a cloud load balancer, or another proxy, confirm forwarded protocol headers are set correctly and Django trusts the right header via SECURE_PROXY_SSL_HEADER.

If your app needs original host preservation through multiple proxy layers, review USE_X_FORWARDED_HOST carefully rather than enabling it by default.

Zero-downtime vs simple restart deployments

A small app can often tolerate a short restart. For larger apps, use a deployment model that keeps old processes serving traffic until new ones pass health checks. Do not assume zero downtime if migrations are blocking.

Handling apps with user uploads

Treat media as persistent data. Do not store uploads inside a release directory that gets replaced on deploy. Use a shared mount or object storage.

Multi-service apps with Celery, Redis, or WebSockets

Include every service in the checklist. A healthy Django web process is only one part of the system if your app also depends on workers, schedulers, Redis, or ASGI or WebSocket components.

For production settings, see Django Production Settings Checklist (DEBUG, ALLOWED_HOSTS, CSRF).

For deployment stack decisions, see Django WSGI vs ASGI: Which One Should You Deploy?.

For file handling in production, see Django Static vs Media Files in Production.

For step-by-step server deployment examples, see deploy Django with Gunicorn and Nginx and deploy Django with Docker Compose.

For recovery planning, see Django deployment rollbacks and recovery.

FAQ

What is the minimum Django deployment checklist for a small app?

At minimum: DEBUG = False, correct ALLOWED_HOSTS, secrets from environment variables, PostgreSQL backups, HTTPS enabled, check --deploy, reviewed migrations, working static files, accessible logs, and a basic rollback plan.

Should I run migrations before or after restarting Gunicorn?

Usually before restarting Gunicorn, so the new process starts against the expected schema. But the safest order depends on whether the release is backward-compatible with the current database state. For risky schema changes, plan the deployment carefully rather than relying on a default order.

What is the safest way to store Django production secrets?

Use a secret manager if available. If not, use environment variables or a protected environment file outside the repository, with restricted permissions and a documented rotation process.

What should I verify immediately after deploying Django?

Check HTTPS, homepage response, login, one write to the database, static asset loading, health endpoint response, worker status, and recent application logs.

Do small Django apps still need TLS, backups, and monitoring?

Yes. Small apps fail in the same ways as larger ones. TLS protects sessions and logins, backups protect your data, and basic monitoring helps you detect failures quickly.

2026 ยท django-deployment.com - Django Deployment knowledge base