docker
Docker Compose for Python/Django
Docker Compose config for Django with PostgreSQL, Celery worker, Redis message broker, and Gunicorn production server.
Overview
A Docker Compose setup for Django projects with PostgreSQL as the database, Redis as a Celery broker, a Celery worker for background tasks, and Gunicorn as the WSGI server. Optimized for development with auto-reload and volume mounts.
Configuration
# docker-compose.yml
services:
# ── Django Web Application ──
web:
build:
context: .
dockerfile: Dockerfile
container_name: django-web
restart: unless-stopped
command: >
sh -c "python manage.py migrate --noinput &&
python manage.py runserver 0.0.0.0:8000"
ports:
- "8000:8000"
environment:
DJANGO_SETTINGS_MODULE: config.settings.development
DATABASE_URL: postgresql://django:${DB_PASSWORD:-secret}@postgres:5432/djangodb
REDIS_URL: redis://redis:6379/0
SECRET_KEY: ${DJANGO_SECRET_KEY:-insecure-dev-key-change-in-prod}
DEBUG: "True"
ALLOWED_HOSTS: "*"
volumes:
- .:/app # Mount code for auto-reload
- static_files:/app/staticfiles # Collected static files
- media_files:/app/media # User-uploaded files
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- backend
# ── Celery Worker ──
celery:
build:
context: .
dockerfile: Dockerfile
container_name: django-celery
restart: unless-stopped
command: celery -A config worker -l INFO --concurrency=2
environment:
DJANGO_SETTINGS_MODULE: config.settings.development
DATABASE_URL: postgresql://django:${DB_PASSWORD:-secret}@postgres:5432/djangodb
REDIS_URL: redis://redis:6379/0
volumes:
- .:/app
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- backend
# ── Celery Beat (Periodic Tasks) ──
celery-beat:
build:
context: .
dockerfile: Dockerfile
container_name: django-celery-beat
restart: unless-stopped
profiles:
- workers # Start with --profile workers
command: celery -A config beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
environment:
DJANGO_SETTINGS_MODULE: config.settings.development
DATABASE_URL: postgresql://django:${DB_PASSWORD:-secret}@postgres:5432/djangodb
REDIS_URL: redis://redis:6379/0
volumes:
- .:/app
depends_on:
- celery
networks:
- backend
# ── PostgreSQL ──
postgres:
image: postgres:16-alpine
container_name: django-postgres
restart: unless-stopped
environment:
POSTGRES_USER: django
POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
POSTGRES_DB: djangodb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U django -d djangodb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
networks:
- backend
# ── Redis ──
redis:
image: redis:7-alpine
container_name: django-redis
restart: unless-stopped
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
volumes:
postgres_data:
static_files:
media_files:
networks:
backend:
driver: bridge
# Dockerfile
FROM python:3.12-slim
# Prevent Python from writing .pyc files and enable unbuffered output
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Install system dependencies for psycopg2
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev gcc && \
rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy project files
COPY . .
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
Key Options Explained
python manage.py migrate --noinput— Runs database migrations automatically on container start. Safe because Django migrations are idempotent.celery -A config worker— Starts a Celery worker that imports tasks from theconfigapp.--concurrency=2limits to 2 parallel task threads.DatabaseScheduler— Celery Beat stores periodic task schedules in the database so they can be managed via Django admin.PYTHONDONTWRITEBYTECODE=1— Prevents.pycfile clutter in volume-mounted directories during development.PYTHONUNBUFFERED=1— Ensures print statements and logs appear immediately indocker compose logs.
Common Modifications
- Production setup: Replace
runserverwithgunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4and setDEBUG=False. - Add Nginx: Place Nginx in front of Gunicorn to serve static files and handle SSL. Mount
static_filesvolume in Nginx. - Add Flower: Include a Flower service for Celery monitoring:
celery -A config flower --port=5555. - Use pip-tools: Replace
requirements.txtwithrequirements.inand compile withpip-compilefor reproducible builds. - Multi-stage build: Add a builder stage to compile dependencies, then copy only the virtual environment to the final image.