Docker ensures consistent environments across development and production.
Dockerfile
dockerfile# Dockerfile FROM python:3.11-slim # Set environment variables ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 ENV PIP_NO_CACHE_DIR=1 # Set work directory WORKDIR /app # Install system dependencies RUN apt-get update && apt-get install -y \ libpq-dev \ gcc \ && rm -rf /var/lib/apt/lists/* # Install Python dependencies COPY requirements.txt . RUN pip install --upgrade pip && \ pip install -r requirements.txt # Copy project COPY . . # Collect static files RUN python manage.py collectstatic --noinput # Create non-root user RUN adduser --disabled-password --gecos '' appuser RUN chown -R appuser:appuser /app USER appuser # Run gunicorn CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
Multi-Stage Build
dockerfile# Dockerfile.multistage # Build stage FROM python:3.11-slim as builder WORKDIR /app RUN apt-get update && apt-get install -y \ libpq-dev gcc COPY requirements.txt . RUN pip wheel --no-cache-dir --wheel-dir /app/wheels -r requirements.txt # Final stage FROM python:3.11-slim WORKDIR /app RUN apt-get update && apt-get install -y libpq5 && \ rm -rf /var/lib/apt/lists/* COPY --from=builder /app/wheels /wheels RUN pip install --no-cache /wheels/* COPY . . RUN adduser --disabled-password appuser && \ chown -R appuser:appuser /app USER appuser RUN python manage.py collectstatic --noinput CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
Docker Compose
yaml# docker-compose.yml version: '3.8' services: web: build: . ports: - "8000:8000" environment: - DJANGO_SETTINGS_MODULE=myproject.settings.production - DATABASE_URL=postgres://postgres:postgres@db:5432/myproject - REDIS_URL=redis://redis:6379/0 depends_on: - db - redis volumes: - static_volume:/app/staticfiles - media_volume:/app/media db: image: postgres:15 volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_DB=myproject - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres redis: image: redis:7-alpine nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - static_volume:/var/www/static - media_volume:/var/www/media depends_on: - web celery: build: . command: celery -A myproject worker -l info environment: - DJANGO_SETTINGS_MODULE=myproject.settings.production - DATABASE_URL=postgres://postgres:postgres@db:5432/myproject - REDIS_URL=redis://redis:6379/0 depends_on: - db - redis volumes: postgres_data: static_volume: media_volume:
Production Docker Compose
yaml# docker-compose.prod.yml version: '3.8' services: web: image: myregistry.com/myproject:${TAG:-latest} restart: always env_file: - .env.production deploy: replicas: 3 resources: limits: memory: 512M healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health/"] interval: 30s timeout: 10s retries: 3
.dockerignore
# .dockerignore
.git
.gitignore
.env*
*.pyc
__pycache__
.pytest_cache
.coverage
htmlcov
*.log
.vscode
.idea
node_modules
media/*
staticfiles/*