docker.recipes

Production Django Stack

advanced

ready Django with Nginx, Gunicorn, PostgreSQL, Redis, and Celery for async tasks

Overview

Django is a high-level Python web framework that follows the model-template-view architectural pattern, originally developed at Lawrence Journal-World newspaper in 2003 and open-sourced in 2005. Known for its 'batteries included' philosophy, Django provides built-in admin interface, ORM, authentication, and security features that enable rapid development of secure, maintainable web applications. This production stack combines Django with Gunicorn as the WSGI server, NGINX as the reverse proxy, PostgreSQL for robust data persistence, Redis for caching and message brokering, and Celery for distributed task processing. This architecture addresses the core challenges of running Django applications at scale: NGINX handles static file serving and SSL termination while reverse proxying dynamic requests to Gunicorn workers, PostgreSQL provides ACID-compliant data storage with advanced querying capabilities, Redis serves dual roles as Django's cache backend and Celery's message broker, while Celery workers process time-intensive tasks asynchronously to prevent request timeouts. The inclusion of Celery Beat enables scheduled task execution, making this stack suitable for applications requiring background job processing, periodic data synchronization, and email sending. This configuration is ideal for startups moving from development to production, established companies deploying content management systems, e-commerce platforms, or SaaS applications that require reliable background task processing. The stack provides the horizontal scaling capabilities needed for growing user bases while maintaining the development velocity that makes Django attractive to Python teams.

Key Features

  • Gunicorn WSGI server with configurable worker processes for handling concurrent Django requests
  • NGINX reverse proxy with static file serving and request buffering for improved performance
  • PostgreSQL ACID-compliant database with JSON support and full-text search capabilities
  • Redis dual-purpose deployment serving as Django cache backend and Celery message broker
  • Celery distributed task queue with separate worker containers for background job processing
  • Celery Beat scheduler for periodic task execution and cron-like job scheduling
  • Multi-container architecture enabling independent scaling of web workers and task processors
  • Shared volume mounts for Django static files and media uploads across containers

Common Use Cases

  • 1Content management systems requiring image processing and thumbnail generation
  • 2E-commerce platforms with order processing, inventory updates, and email notifications
  • 3SaaS applications needing user onboarding workflows and scheduled data exports
  • 4News or blog sites with search indexing and scheduled content publication
  • 5Financial applications requiring background report generation and data aggregation
  • 6Social platforms with notification systems and periodic cleanup tasks
  • 7Multi-tenant applications requiring tenant-specific background processing

Prerequisites

  • Minimum 2GB RAM to accommodate PostgreSQL (1GB), Redis (512MB), and multiple Python processes
  • Port 80 available for NGINX HTTP traffic (configurable via NGINX_PORT environment variable)
  • Django application with properly configured settings.py for database and Redis connections
  • Understanding of Celery task decorators and Django's collectstatic command
  • Familiarity with Gunicorn worker configuration and Django WSGI deployment
  • Basic knowledge of PostgreSQL user management and database creation

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 nginx:
3 image: nginx:alpine
4 container_name: django-nginx
5 restart: unless-stopped
6 ports:
7 - "${NGINX_PORT:-80}:80"
8 volumes:
9 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
10 - ./static:/app/static:ro
11 - ./media:/app/media:ro
12 depends_on:
13 - web
14
15 web:
16 build:
17 context: ./app
18 dockerfile: Dockerfile
19 container_name: django-web
20 restart: unless-stopped
21 command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
22 volumes:
23 - ./app:/app
24 - ./static:/app/static
25 - ./media:/app/media
26 environment:
27 - DEBUG=${DEBUG:-False}
28 - SECRET_KEY=${SECRET_KEY}
29 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
30 - REDIS_URL=redis://redis:6379/0
31 - CELERY_BROKER_URL=redis://redis:6379/1
32 depends_on:
33 - db
34 - redis
35
36 celery:
37 build:
38 context: ./app
39 dockerfile: Dockerfile
40 container_name: django-celery
41 restart: unless-stopped
42 command: celery -A config worker -l info
43 volumes:
44 - ./app:/app
45 environment:
46 - DEBUG=${DEBUG:-False}
47 - SECRET_KEY=${SECRET_KEY}
48 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
49 - REDIS_URL=redis://redis:6379/0
50 - CELERY_BROKER_URL=redis://redis:6379/1
51 depends_on:
52 - db
53 - redis
54
55 celery-beat:
56 build:
57 context: ./app
58 dockerfile: Dockerfile
59 container_name: django-celery-beat
60 restart: unless-stopped
61 command: celery -A config beat -l info
62 volumes:
63 - ./app:/app
64 environment:
65 - DEBUG=${DEBUG:-False}
66 - SECRET_KEY=${SECRET_KEY}
67 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
68 - CELERY_BROKER_URL=redis://redis:6379/1
69 depends_on:
70 - celery
71
72 db:
73 image: postgres:16-alpine
74 container_name: django-db
75 restart: unless-stopped
76 environment:
77 - POSTGRES_USER=${DB_USER}
78 - POSTGRES_PASSWORD=${DB_PASSWORD}
79 - POSTGRES_DB=${DB_NAME}
80 volumes:
81 - postgres_data:/var/lib/postgresql/data
82
83 redis:
84 image: redis:7-alpine
85 container_name: django-redis
86 restart: unless-stopped
87 volumes:
88 - redis_data:/data
89
90volumes:
91 postgres_data:
92 redis_data:

.env Template

.env
1# Django Production Stack
2NGINX_PORT=80
3DEBUG=False
4
5# Security
6SECRET_KEY=your-super-secret-key-change-in-production
7
8# Database
9DB_USER=django
10DB_PASSWORD=django_password
11DB_NAME=django_db

Usage Notes

  1. 1Create ./app/Dockerfile with your Django app
  2. 2Configure nginx.conf for static files and proxy
  3. 3Run migrations: docker compose exec web python manage.py migrate
  4. 4Collect static: docker compose exec web python manage.py collectstatic
  5. 5Celery handles async tasks, beat handles scheduled tasks
  6. 6Scale workers: docker compose up -d --scale celery=3

Individual Services(6 services)

Copy individual services to mix and match with your existing compose files.

nginx
nginx:
  image: nginx:alpine
  container_name: django-nginx
  restart: unless-stopped
  ports:
    - ${NGINX_PORT:-80}:80
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./static:/app/static:ro
    - ./media:/app/media:ro
  depends_on:
    - web
web
web:
  build:
    context: ./app
    dockerfile: Dockerfile
  container_name: django-web
  restart: unless-stopped
  command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
  volumes:
    - ./app:/app
    - ./static:/app/static
    - ./media:/app/media
  environment:
    - DEBUG=${DEBUG:-False}
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379/0
    - CELERY_BROKER_URL=redis://redis:6379/1
  depends_on:
    - db
    - redis
celery
celery:
  build:
    context: ./app
    dockerfile: Dockerfile
  container_name: django-celery
  restart: unless-stopped
  command: celery -A config worker -l info
  volumes:
    - ./app:/app
  environment:
    - DEBUG=${DEBUG:-False}
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379/0
    - CELERY_BROKER_URL=redis://redis:6379/1
  depends_on:
    - db
    - redis
celery-beat
celery-beat:
  build:
    context: ./app
    dockerfile: Dockerfile
  container_name: django-celery-beat
  restart: unless-stopped
  command: celery -A config beat -l info
  volumes:
    - ./app:/app
  environment:
    - DEBUG=${DEBUG:-False}
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - CELERY_BROKER_URL=redis://redis:6379/1
  depends_on:
    - celery
db
db:
  image: postgres:16-alpine
  container_name: django-db
  restart: unless-stopped
  environment:
    - POSTGRES_USER=${DB_USER}
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=${DB_NAME}
  volumes:
    - postgres_data:/var/lib/postgresql/data
redis
redis:
  image: redis:7-alpine
  container_name: django-redis
  restart: unless-stopped
  volumes:
    - redis_data:/data

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 nginx:
5 image: nginx:alpine
6 container_name: django-nginx
7 restart: unless-stopped
8 ports:
9 - "${NGINX_PORT:-80}:80"
10 volumes:
11 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
12 - ./static:/app/static:ro
13 - ./media:/app/media:ro
14 depends_on:
15 - web
16
17 web:
18 build:
19 context: ./app
20 dockerfile: Dockerfile
21 container_name: django-web
22 restart: unless-stopped
23 command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
24 volumes:
25 - ./app:/app
26 - ./static:/app/static
27 - ./media:/app/media
28 environment:
29 - DEBUG=${DEBUG:-False}
30 - SECRET_KEY=${SECRET_KEY}
31 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
32 - REDIS_URL=redis://redis:6379/0
33 - CELERY_BROKER_URL=redis://redis:6379/1
34 depends_on:
35 - db
36 - redis
37
38 celery:
39 build:
40 context: ./app
41 dockerfile: Dockerfile
42 container_name: django-celery
43 restart: unless-stopped
44 command: celery -A config worker -l info
45 volumes:
46 - ./app:/app
47 environment:
48 - DEBUG=${DEBUG:-False}
49 - SECRET_KEY=${SECRET_KEY}
50 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
51 - REDIS_URL=redis://redis:6379/0
52 - CELERY_BROKER_URL=redis://redis:6379/1
53 depends_on:
54 - db
55 - redis
56
57 celery-beat:
58 build:
59 context: ./app
60 dockerfile: Dockerfile
61 container_name: django-celery-beat
62 restart: unless-stopped
63 command: celery -A config beat -l info
64 volumes:
65 - ./app:/app
66 environment:
67 - DEBUG=${DEBUG:-False}
68 - SECRET_KEY=${SECRET_KEY}
69 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
70 - CELERY_BROKER_URL=redis://redis:6379/1
71 depends_on:
72 - celery
73
74 db:
75 image: postgres:16-alpine
76 container_name: django-db
77 restart: unless-stopped
78 environment:
79 - POSTGRES_USER=${DB_USER}
80 - POSTGRES_PASSWORD=${DB_PASSWORD}
81 - POSTGRES_DB=${DB_NAME}
82 volumes:
83 - postgres_data:/var/lib/postgresql/data
84
85 redis:
86 image: redis:7-alpine
87 container_name: django-redis
88 restart: unless-stopped
89 volumes:
90 - redis_data:/data
91
92volumes:
93 postgres_data:
94 redis_data:
95EOF
96
97# 2. Create the .env file
98cat > .env << 'EOF'
99# Django Production Stack
100NGINX_PORT=80
101DEBUG=False
102
103# Security
104SECRET_KEY=your-super-secret-key-change-in-production
105
106# Database
107DB_USER=django
108DB_PASSWORD=django_password
109DB_NAME=django_db
110EOF
111
112# 3. Start the services
113docker compose up -d
114
115# 4. View logs
116docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/production-django-stack/run | bash

Troubleshooting

  • 502 Bad Gateway from NGINX: Check if Gunicorn is running on port 8000 and web container is healthy
  • Celery workers not processing tasks: Verify CELERY_BROKER_URL points to Redis and tasks are properly registered
  • Static files not loading: Run 'docker compose exec web python manage.py collectstatic' and check NGINX volume mounts
  • PostgreSQL connection refused: Ensure DATABASE_URL environment variable matches POSTGRES_* settings in db service
  • High memory usage: Reduce Gunicorn workers from 4 to 2-3 or implement connection pooling for PostgreSQL
  • Celery Beat duplicate tasks: Only run one celery-beat container and check for proper task scheduling configuration

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space