On this page
article
Deploying Django
Deploy Django to production — Gunicorn, Nginx, static files, environment config, PostgreSQL, and Docker deployment.
Moving Django from runserver to production requires a WSGI server, reverse proxy, database, and proper configuration.
Production Settings Checklist
# settings/production.py
import os
DEBUG = False
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
SECRET_KEY = os.environ["SECRET_KEY"]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["DB_NAME"],
"USER": os.environ["DB_USER"],
"PASSWORD": os.environ["DB_PASSWORD"],
"HOST": os.environ.get("DB_HOST", "localhost"),
"PORT": os.environ.get("DB_PORT", "5432"),
}
}
STATIC_ROOT = "/app/staticfiles"
MEDIA_ROOT = "/app/media"
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
Gunicorn WSGI Server
pip install gunicorn
gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 4
Worker count formula: (2 × CPU cores) + 1
Nginx Reverse Proxy
server {
listen 80;
server_name example.com;
location /static/ {
alias /app/staticfiles/;
}
location /media/ {
alias /app/media/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Collect Static Files
python manage.py collectstatic --noinput
For cloud storage (S3):
pip install django-storages boto3
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
STATICFILES_STORAGE = "storages.backends.s3boto3.S3StaticStorage"
AWS_STORAGE_BUCKET_NAME = "my-bucket"
Docker Deployment
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "mysite.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]
# docker-compose.yml
services:
web:
build: .
ports:
- "8000:8000"
env_file: .env
depends_on:
- db
command: >
sh -c "python manage.py migrate &&
gunicorn mysite.wsgi:application --bind 0.0.0.0:8000"
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Database Migrations in Production
python manage.py migrate --noinput
Run migrations as a separate deployment step, not inside the web container startup in high-traffic environments.
Monitoring
# settings.py
LOGGING = {
"version": 1,
"handlers": {
"console": {"class": "logging.StreamHandler"},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
"loggers": {
"django": {"handlers": ["console"], "level": "INFO", "propagate": False},
},
}
Add health check endpoint:
from django.http import JsonResponse
def health(request):
return JsonResponse({"status": "ok"})
Deployment Platforms
| Platform | Best For |
|---|---|
| Railway / Render | Simple PaaS deployment |
| AWS (ECS/EB) | Full AWS integration |
| Google Cloud Run | Container-based, auto-scaling |
| DigitalOcean App Platform | Managed containers |
Production Django is battle-tested — Instagram, Pinterest, and Dropbox all run on it at massive scale.