Skip to content

How to Deploy a Django App to Production (2026 Guide)

Deploy Django with Gunicorn, PostgreSQL, WhiteNoise static files and automatic migrations: production settings, ALLOWED_HOSTS, CSRF and a one-command deploy.

Published
Reading time
4 min
On this page

Key takeaways

  • Production Django needs four settings from the environment: SECRET_KEY, DEBUG=False, ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS.
  • Read the database from DATABASE_URL with dj-database-url, and serve static files with WhiteNoise.
  • On DockHosting, Gunicorn is configured for you and manage.py migrate runs automatically before traffic switches.

To deploy a Django app to production you need five things: settings read from the environment (SECRET_KEY, DEBUG=False, ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS), a WSGI server (Gunicorn), a production database read from DATABASE_URL with dj-database-url, static files served by WhiteNoise after collectstatic, and migrations that run before traffic switches. On DockHosting, Gunicorn and manage.py migrate are handled automatically, so the deploy itself is one command: dock deploy.

1. Production settings from the environment

Hard-coded secrets and DEBUG = True are the two classic Django deploy mistakes. Read both from the environment:

python
# settings.py
import os
import dj_database_url
 
SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = os.environ.get("DEBUG", "False") == "True"
 
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
CSRF_TRUSTED_ORIGINS = [f"https://{h}" for h in ALLOWED_HOSTS if h]
 
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}

Generate a strong key once and store it as a secret:

bash
python -c "import secrets; print(secrets.token_urlsafe(50))"

2. Static files with WhiteNoise

With DEBUG=False, Django stops serving CSS and JavaScript (including the admin's). WhiteNoise serves them efficiently from your app:

python
# settings.py
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",  # right after SecurityMiddleware
    # …
]
 
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}

Files have to be collected at build time. Add collectstatic to your project's build command in the dashboard:

bash
python manage.py collectstatic --noinput

3. Dependencies

text
# requirements.txt
django
gunicorn
dj-database-url
psycopg[binary]
whitenoise

Pin versions (pip freeze > requirements.txt) so production installs exactly what you tested. Poetry users can keep pyproject.toml: it's detected too.

4. Deploy

bash
curl -fsSL https://cli.dockhosting.dev/install.sh | sh   # Windows: irm https://cli.dockhosting.dev/install.ps1 | iex
dock login
dock deploy

What happens:

  1. manage.py marks the project as Django, on Python 3.11 or 3.12.
  2. Dependencies are installed with pip or Poetry.
  3. Gunicorn workers are configured for you, so you don't need a Procfile.
  4. python manage.py migrate runs automatically before traffic switches to the new release, so your schema is never behind your code.
  5. You get https://<project>.dockhosting.cloud with automatic HTTPS.

Set SECRET_KEY and ALLOWED_HOSTS (for example myapp.dockhosting.cloud) in the project's environment variables.

5. Add PostgreSQL

Create a managed PostgreSQL database in the dashboard and attach it to the project. DATABASE_URL is injected over a private network, and the dj_database_url.config() line above picks it up with no code changes. Create your admin user from your machine through public database access (Hobby and up), or with a data migration.

Need Redis for caching or Celery? Attach managed Redis and read REDIS_URL.

6. Production checklist

SettingValue
DEBUGFalse
SECRET_KEYFrom the environment, 50+ random characters
ALLOWED_HOSTSYour .dockhosting.cloud host and custom domains
CSRF_TRUSTED_ORIGINSThe same hosts, with https://
SECURE_PROXY_SSL_HEADER("HTTP_X_FORWARDED_PROTO", "https") if you use SECURE_SSL_REDIRECT
Static filesWhiteNoise + collectstatic in the build

Run python manage.py check --deploy locally. It flags most of these for you.

Troubleshooting

  • DisallowedHost: add the host to ALLOWED_HOSTS.
  • CSRF failed on login or forms: add the https:// origin to CSRF_TRUSTED_ORIGINS.
  • 502 after deploy: see 8 causes of a 502 and how to fix them.

Prefer async APIs? See deploying FastAPI.

Frequently asked questions

Do I need a Procfile or Dockerfile to deploy Django?

No. DockHosting detects Django from manage.py, installs dependencies with pip (requirements.txt) or Poetry (pyproject.toml), runs Gunicorn workers and runs migrate before the new release takes traffic.

Which Python versions are supported?

Python 3.11 and 3.12.

Why do I get 'CSRF verification failed' in production?

Since Django 4.0, CSRF checks the Origin header against CSRF_TRUSTED_ORIGINS, which must include the scheme, for example https://myapp.dockhosting.cloud. Add every domain your site is served from.

Why are my static files (CSS, admin styles) missing?

With DEBUG=False, Django no longer serves static files. Add WhiteNoise middleware and make sure collectstatic runs during the build.

  • Django
  • Python
  • PostgreSQL
  • Deployment
  • Tutorial

Written by Doughmi ILYAS

Doughmi builds DockHosting, the developer-first platform behind dock deploy: the build system, the managed databases and the dashboard. On this blog: guides to shipping apps without running servers, written with the commands the platform actually ships.

Summarize with

Share

All posts

Create an account, run dock deploy, and get a live HTTPS URL in minutes.