Docker & Production Config

Difficulty: Advanced

Deploying Django to production requires several considerations beyond development setup. You need a production-ready WSGI server (Gunicorn or uWSGI), proper environment variable management, security settings, and often containerization with Docker.

Gunicorn is the most popular WSGI server for Django. Unlike Django's development server (runserver), Gunicorn is multi-process, handles concurrent requests efficiently, and is designed for production use. Install with 'pip install gunicorn' and run with 'gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 3'.

The number of Gunicorn workers should typically be (2 * CPU cores) + 1. Each worker handles requests independently. For I/O-bound applications (like most Django apps), you can also use async workers with 'gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker'.

Environment variables separate configuration from code. Use python-decouple or django-environ to read environment variables with type casting and defaults. Store sensitive data (SECRET_KEY, database credentials, API keys) in environment variables or .env files that are not committed to version control.

Django's deployment checklist (accessible via 'python manage.py check --deploy') verifies common security settings. Critical settings include: DEBUG=False, secure SECRET_KEY, ALLOWED_HOSTS configured, HTTPS settings (SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS), CSRF_COOKIE_SECURE, SESSION_COOKIE_SECURE, and X_FRAME_OPTIONS.

Docker containerizes your application for consistent deployment across environments. A Django Dockerfile typically uses a Python base image, installs dependencies, copies the application code, runs collectstatic, and starts Gunicorn. Docker Compose orchestrates multiple services (Django, PostgreSQL, Redis, Nginx).

Nginx is commonly used as a reverse proxy in front of Gunicorn. It handles SSL termination, serves static and media files directly (much faster than Python), provides load balancing, and buffers slow client connections so Gunicorn workers are freed quickly.

Code examples

Gunicorn Configuration

# Install
# pip install gunicorn

# Basic run
# gunicorn myproject.wsgi:application --bind 0.0.0.0:8000

# Production run with workers
# gunicorn myproject.wsgi:application \
#   --bind 0.0.0.0:8000 \
#   --workers 3 \
#   --timeout 120 \
#   --access-logfile - \
#   --error-logfile -

# gunicorn.conf.py
import multiprocessing

bind = '0.0.0.0:8000'
workers = multiprocessing.cpu_count() * 2 + 1
timeout = 120
accesslog = '-'
errorlog = '-'
loglevel = 'info'
worker_class = 'sync'  # or 'uvicorn.workers.UvicornWorker' for async

# Run with config file:
# gunicorn myproject.wsgi:application -c gunicorn.conf.py

Gunicorn replaces Django's runserver for production. Workers handle concurrent requests. The formula (2 * cores + 1) balances memory usage and concurrency. Logs go to stdout (-) for container environments.

Environment Variables

# pip install python-decouple

# .env file (NOT committed to git)
# SECRET_KEY=django-insecure-abc123def456
# DEBUG=False
# DATABASE_URL=postgres://user:pass@localhost:5432/mydb
# ALLOWED_HOSTS=myapp.com,www.myapp.com
# AWS_ACCESS_KEY_ID=AKIA...
# AWS_SECRET_ACCESS_KEY=...

# settings.py
from decouple import config, Csv
import dj_database_url

SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost', cast=Csv())

DATABASES = {
    'default': dj_database_url.config(
        default='sqlite:///db.sqlite3',
        conn_max_age=600,
    )
}

# .gitignore
# .env
# *.pyc
# db.sqlite3
# media/
# staticfiles/

python-decouple reads environment variables with type casting. dj-database-url parses database URLs into Django's DATABASES format. Never commit .env files or secrets to version control.

Dockerfile

# Dockerfile
FROM python:3.12-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set work directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy project
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput

# Run Gunicorn
CMD ["gunicorn", "myproject.wsgi:application", \
     "--bind", "0.0.0.0:8000", \
     "--workers", "3"]

# docker-compose.yml
# services:
#   web:
#     build: .
#     ports:
#       - "8000:8000"
#     env_file: .env
#     depends_on:
#       - db
#       - redis
#   db:
#     image: postgres:16
#     environment:
#       POSTGRES_DB: mydb
#       POSTGRES_USER: myuser
#       POSTGRES_PASSWORD: mypass
#     volumes:
#       - postgres_data:/var/lib/postgresql/data
#   redis:
#     image: redis:7-alpine
# volumes:
#   postgres_data:

The Dockerfile uses a slim Python image, installs dependencies, copies code, runs collectstatic, and starts Gunicorn. Docker Compose orchestrates the web app, PostgreSQL database, and Redis cache as linked services.

Production Security Checklist

# settings/production.py
from .base import *

DEBUG = False

# Security
SECRET_KEY = config('SECRET_KEY')
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())

# HTTPS settings
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# Cookie security
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'

# Run the deployment check:
# python manage.py check --deploy
#
# This checks for:
# - DEBUG is False
# - SECRET_KEY is not default
# - ALLOWED_HOSTS is set
# - Security middleware is configured
# - HTTPS settings are enabled
# - Cookie security is configured

Production settings disable DEBUG, enforce HTTPS, secure cookies, and add HSTS headers. 'manage.py check --deploy' verifies these settings. Use separate settings files for development and production.

Key points

Concepts covered

Docker, Gunicorn, Nginx, Environment Variables, Production Checklist