Skip to main content

Command Palette

Search for a command to run...

Complete Docker Guide for Django & DRF

What is Docker and Why Use It?

Published
22 min read
Complete Docker Guide for Django & DRF

Docker in a Nutshell:

Docker is a containerization platform that packages applications and their dependencies into standardized units called containers.

Traditional vs Dockerized Deployment:

TRADITIONAL: "It works on my machine!"
Developer Machine → "Works!" 
Production Server → "But not here! 😭"

DOCKERIZED: "It works everywhere!"
Developer Machine (Container) → "Works!" 
CI/CD Pipeline (Container) → "Works!" 
Production Server (Container) → "Works! 🎉"

Key Benefits for Django Development:

BenefitDescription
ConsistencySame environment everywhere
IsolationNo "dependency hell"
ReproducibilityRecreate exact environment
ScalabilityEasy to scale horizontally
Version ControlTrack environment changes
MicroservicesBreak app into independent services

🏗️ Docker Core Concepts

1. Docker Images vs Containers:

  • Image: Blueprint/recipe (like a class in OOP)

  • Container: Running instance (like an object instance)

2. Dockerfile: Instructions to build an image

3. Docker Compose: Orchestrate multi-container apps

4. Volumes: Persistent data storage

5. Networks: Communication between containers


🚀 Quick Start: Simple Django + Docker

Project Structure:

django_docker_project/
├── .dockerignore
├── .env
├── .env.example
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── manage.py
├── entrypoint.sh
├── core/
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── apps/
│   └── api/
└── static/

Step 1: Basic Dockerfile

# Dockerfile
# ------------- STAGE 1: Builder -------------
FROM python:3.11-slim as builder

# Set work directory
WORKDIR /app

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

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

# Install Python dependencies
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt


# ------------- STAGE 2: Final -------------
FROM python:3.11-slim

# Create non-root user
RUN addgroup --system django && \
    adduser --system --ingroup django django

# Create directory for app
WORKDIR /home/django/app

# Install dependencies
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*

# Copy project
COPY --chown=django:django . .

# Switch to non-root user
USER django

# Run entrypoint
COPY --chown=django:django entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/home/django/app/entrypoint.sh"]

Step 2: Entrypoint Script

#!/bin/bash
# entrypoint.sh

set -o errexit
set -o pipefail
set -o nounset

# Function to check if PostgreSQL is ready
postgres_ready() {
    python << END
import sys
import psycopg2
from decouple import config

try:
    psycopg2.connect(
        dbname=config("DB_NAME"),
        user=config("DB_USER"),
        password=config("DB_PASSWORD"),
        host=config("DB_HOST"),
        port=config("DB_PORT", default=5432),
    )
except psycopg2.OperationalError:
    sys.exit(-1)
sys.exit(0)
END
}

# Function to check if Redis is ready
redis_ready() {
    python << END
import sys
import redis
from decouple import config

try:
    redis_client = redis.Redis(
        host=config("REDIS_HOST", default="redis"),
        port=config("REDIS_PORT", default=6379),
        db=config("REDIS_DB", default=0),
        socket_connect_timeout=5
    )
    redis_client.ping()
except redis.exceptions.ConnectionError:
    sys.exit(-1)
sys.exit(0)
END
}

# Wait for PostgreSQL
until postgres_ready; do
    echo "Waiting for PostgreSQL to become available..."
    sleep 2
done
echo "PostgreSQL is available!"

# Wait for Redis (if needed)
until redis_ready; do
    echo "Waiting for Redis to become available..."
    sleep 2
done
echo "Redis is available!"

# Run migrations
python manage.py migrate --noinput

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

# Create superuser if doesn't exist (for development)
if [ "$DJANGO_SUPERUSER_EMAIL" ] && [ "$DJANGO_SUPERUSER_USERNAME" ]; then
    python manage.py createsuperuser \
        --noinput \
        --email "$DJANGO_SUPERUSER_EMAIL" \
        --username "$DJANGO_SUPERUSER_USERNAME" || true
fi

# Execute command passed to docker run
exec "$@"

Step 3: Docker Compose for Development

# docker-compose.yml
version: '3.8'

services:
  # Django Application
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/home/django/app
      - static_volume:/home/django/app/staticfiles
      - media_volume:/home/django/app/media
    ports:
      - "8000:8000"
    env_file:
      - .env
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.dev
    depends_on:
      - db
      - redis
    restart: unless-stopped
    networks:
      - django_network

  # PostgreSQL Database
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    ports:
      - "5432:5432"
    networks:
      - django_network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis for caching and Celery
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    networks:
      - django_network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Celery Worker
  celery_worker:
    build: .
    command: celery -A core worker --loglevel=info
    volumes:
      - .:/home/django/app
    env_file:
      - .env
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.dev
    depends_on:
      - db
      - redis
      - web
    networks:
      - django_network
    restart: unless-stopped

  # Celery Beat (for scheduled tasks)
  celery_beat:
    build: .
    command: celery -A core beat --loglevel=info
    volumes:
      - .:/home/django/app
    env_file:
      - .env
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.dev
    depends_on:
      - db
      - redis
      - web
    networks:
      - django_network
    restart: unless-stopped

  # Nginx for production-like setup
  nginx:
    image: nginx:1.25-alpine
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - static_volume:/home/django/app/staticfiles:ro
      - media_volume:/home/django/app/media:ro
    ports:
      - "80:80"
    depends_on:
      - web
    networks:
      - django_network

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:

networks:
  django_network:
    driver: bridge

Step 4: Environment Variables

# .env.example
# Copy to .env and fill in values

# Django
SECRET_KEY=your-secret-key-here
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_SETTINGS_MODULE=core.settings.dev

# Database
DB_NAME=django_db
DB_USER=django_user
DB_PASSWORD=django_password
DB_HOST=db
DB_PORT=5432

# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=0

# Celery
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

# Superuser (for development)
DJANGO_SUPERUSER_EMAIL=admin@example.com
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_PASSWORD=admin123

# Email (for production)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password

# CORS
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000

Step 5: Docker Ignore File

# .dockerignore

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
env/
.venv/

# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media/

# Coverage
.coverage
htmlcov/

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Docker
Dockerfile
docker-compose*.yml
.env

⚡ Advanced Django Docker Configuration

Multi-stage Dockerfile for Production

# Dockerfile.prod
# ------------- STAGE 1: Builder -------------
FROM python:3.11-slim as builder

WORKDIR /app

# Install system dependencies for building
RUN apt-get update && apt-get install -y \
    gcc \
    g++ \
    libpq-dev \
    libjpeg-dev \
    libpng-dev \
    libwebp-dev \
    zlib1g-dev \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# Install Python dependencies
COPY requirements/prod.txt .
RUN pip install --upgrade pip && \
    pip install --no-cache-dir -r prod.txt


# ------------- STAGE 2: Runtime -------------
FROM python:3.11-slim

# Install runtime dependencies only
RUN apt-get update && apt-get install -y \
    libpq-dev \
    libjpeg-dev \
    libpng-dev \
    libwebp-dev \
    nginx \
    gettext \
    && rm -rf /var/lib/apt/lists/*

# Copy virtual environment from builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# Create non-root user
RUN addgroup --system --gid 1001 django && \
    adduser --system --uid 1001 --gid 1001 django

# Create app directory
WORKDIR /home/django/app

# Copy application
COPY --chown=django:django . .

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

# Configure nginx
COPY --chown=django:django nginx/nginx.conf /etc/nginx/nginx.conf
COPY --chown=django:django nginx/django.conf /etc/nginx/sites-available/django
RUN ln -s /etc/nginx/sites-available/django /etc/nginx/sites-enabled/ && \
    rm /etc/nginx/sites-enabled/default

# Switch to non-root user
USER django

# Expose port
EXPOSE 8000

# Run Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "--threads", "2", "core.wsgi:application"]

Production Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  # Django with Gunicorn
  web:
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: gunicorn --bind 0.0.0.0:8000 --workers 4 --threads 2 --access-logfile - core.wsgi:application
    expose:
      - 8000
    volumes:
      - static_volume:/home/django/app/staticfiles
      - media_volume:/home/django/app/media
    env_file:
      - .env.prod
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.production
    depends_on:
      - db
      - redis
    restart: always
    networks:
      - backend
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Nginx
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - static_volume:/home/django/app/staticfiles:ro
      - media_volume:/home/django/app/media:ro
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - web
    networks:
      - backend
      - frontend
    restart: always

  # PostgreSQL
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./postgres/backups:/backups
    env_file:
      - .env.prod
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
    command: >
      postgres -c max_connections=200
               -c shared_buffers=256MB
               -c effective_cache_size=1GB
    networks:
      - backend
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis
  redis:
    image: redis:7-alpine
    command: >
      redis-server 
      --appendonly yes 
      --maxmemory 256mb 
      --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
    networks:
      - backend
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Celery Worker
  celery_worker:
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: celery -A core worker --loglevel=info --concurrency=4
    volumes:
      - media_volume:/home/django/app/media
    env_file:
      - .env.prod
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.production
    depends_on:
      - db
      - redis
      - web
    networks:
      - backend
    restart: always
    deploy:
      replicas: 2  # Scale to 2 workers

  # Celery Beat
  celery_beat:
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: celery -A core beat --loglevel=info
    env_file:
      - .env.prod
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.production
    depends_on:
      - db
      - redis
      - web
    networks:
      - backend
    restart: always

  # Flower for monitoring Celery
  flower:
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: celery -A core flower --port=5555
    ports:
      - "5555:5555"
    env_file:
      - .env.prod
    environment:
      - DJANGO_SETTINGS_MODULE=core.settings.production
    depends_on:
      - celery_worker
      - redis
    networks:
      - backend
    restart: always

  # pgAdmin (Database GUI - optional)
  pgadmin:
    image: dpage/pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=admin@example.com
      - PGADMIN_DEFAULT_PASSWORD=admin123
      - PGADMIN_CONFIG_SERVER_MODE=False
    ports:
      - "5050:80"
    volumes:
      - pgadmin_data:/var/lib/pgadmin
      - ./pgadmin/servers.json:/pgadmin4/servers.json:ro
    networks:
      - backend
    restart: always

  # Traefik as reverse proxy (alternative to nginx)
  traefik:
    image: traefik:v3.0
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=admin@example.com"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Traefik dashboard
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt:/letsencrypt"
    networks:
      - frontend
    restart: always

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:
  pgadmin_data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

🔧 Django Settings for Docker

Multi-environment Settings Structure:

# core/settings/__init__.py
import os

# Import the appropriate settings based on environment
environment = os.environ.get('DJANGO_SETTINGS_MODULE', 'core.settings.dev')

if environment == 'core.settings.production':
    from .production import *
else:
    from .dev import *

Development Settings:

# core/settings/dev.py
import os
from pathlib import Path
from decouple import config

BASE_DIR = Path(__file__).resolve().parent.parent.parent

# Security
SECRET_KEY = config('SECRET_KEY', default='dev-secret-key')
DEBUG = True
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1').split(',')

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME', default='django_db'),
        'USER': config('DB_USER', default='django_user'),
        'PASSWORD': config('DB_PASSWORD', default='django_password'),
        'HOST': config('DB_HOST', default='db'),
        'PORT': config('DB_PORT', default=5432),
    }
}

# Cache
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': f"redis://{config('REDIS_HOST', default='redis')}:{config('REDIS_PORT', default=6379)}/1",
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        },
        'KEY_PREFIX': 'django_dev',
    }
}

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# CORS
CORS_ALLOWED_ORIGINS = config(
    'CORS_ALLOWED_ORIGINS', 
    default='http://localhost:3000,http://127.0.0.1:3000'
).split(',')

# Email (Console for development)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

# Celery
CELERY_BROKER_URL = config('CELERY_BROKER_URL', default='redis://redis:6379/0')
CELERY_RESULT_BACKEND = config('CELERY_RESULT_BACKEND', default='redis://redis:6379/0')
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'UTC'

# DRF
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
}

# Debug Toolbar (only in dev)
if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']
    MIDDLEWARE.insert(0, 'debug_toolbar.middleware.DebugToolbarMiddleware')
    INTERNAL_IPS = ['127.0.0.1', 'localhost']
    DEBUG_TOOLBAR_CONFIG = {
        'SHOW_TOOLBAR_CALLBACK': lambda request: True,
    }

Production Settings:

# core/settings/production.py
import os
from pathlib import Path
from decouple import config
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

BASE_DIR = Path(__file__).resolve().parent.parent.parent

# Security
SECRET_KEY = config('SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = config('ALLOWED_HOSTS').split(',')

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME'),
        'USER': config('DB_USER'),
        'PASSWORD': config('DB_PASSWORD'),
        'HOST': config('DB_HOST'),
        'PORT': config('DB_PORT', default=5432),
        'CONN_MAX_AGE': 600,
        'OPTIONS': {
            'sslmode': 'require' if config('DB_SSL', default=False, cast=bool) else 'disable',
        },
    }
}

# Cache
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': config('REDIS_URL'),
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'CONNECTION_POOL_KWARGS': {
                'max_connections': 50,
                'retry_on_timeout': True,
            },
            'COMPRESSOR': 'django_redis.compressors.zlib.ZlibCompressor',
        },
        'KEY_PREFIX': 'django_prod',
    }
}

# Static files (served by nginx)
STATIC_URL = '/static/'
STATIC_ROOT = '/home/django/app/staticfiles'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = '/home/django/app/media'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'  # For S3

# Security headers
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# CORS
CORS_ALLOWED_ORIGINS = config('CORS_ALLOWED_ORIGINS').split(',')
CORS_ALLOW_CREDENTIALS = True

# Email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = config('EMAIL_HOST')
EMAIL_PORT = config('EMAIL_PORT', cast=int)
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', default=EMAIL_HOST_USER)

# Celery
CELERY_BROKER_URL = config('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = config('CELERY_RESULT_BACKEND')
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'UTC'
CELERY_TASK_ACKS_LATE = True
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
CELERY_TASK_REJECT_ON_WORKER_LOST = True

# DRF
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
    },
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 50,
}

# Sentry
if config('SENTRY_DSN', default=''):
    sentry_sdk.init(
        dsn=config('SENTRY_DSN'),
        integrations=[DjangoIntegration()],
        traces_sample_rate=0.1,
        send_default_pii=True,
        environment=config('SENTRY_ENVIRONMENT', default='production'),
    )

# Logging
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/django.log',
            'maxBytes': 1024 * 1024 * 5,  # 5 MB
            'backupCount': 5,
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': True,
        },
        'django.request': {
            'handlers': ['console', 'file'],
            'level': 'ERROR',
            'propagate': False,
        },
        'myapp': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
}

🌐 Nginx Configuration

Nginx Config for Django:

# nginx/nginx.conf
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    # Basic settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/m;

    # Include site configurations
    include /etc/nginx/sites-enabled/*;
}

Django Site Configuration:

# nginx/sites-available/django
# HTTP Server
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

# HTTPS Server
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name yourdomain.com www.yourdomain.com;

    # SSL certificates
    ssl_certificate /etc/nginx/ssl/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/yourdomain.com/privkey.pem;

    # SSL optimization
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Static files
    location /static/ {
        alias /home/django/app/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Media files
    location /media/ {
        alias /home/django/app/media/;
        expires 30d;
        add_header Cache-Control "public";
        access_log off;
    }

    # API endpoints with rate limiting
    location /api/ {
        # Rate limiting for API
        limit_req zone=api_limit burst=20 nodelay;

        proxy_pass http://web: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;

        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }

    # Authentication endpoints with stricter limits
    location ~ ^/api/(auth|login|register|password-reset)/ {
        limit_req zone=auth_limit burst=5 nodelay;

        proxy_pass http://web: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;
    }

    # Admin panel
    location /admin/ {
        proxy_pass http://web: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;

        # Basic authentication for admin (optional extra layer)
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }

    # Main Django app
    location / {
        proxy_pass http://web: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;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Health check endpoint
    location /health/ {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
}

🔐 Security Configuration

Production Entrypoint with Security:

#!/bin/bash
# entrypoint.prod.sh

set -e

# Run as non-root user
if [ "$(id -u)" = '0' ]; then
    # Change ownership of app directory
    chown -R django:django /home/django/app

    # Drop privileges
    exec gosu django "$0" "$@"
fi

# Wait for database
echo "Waiting for PostgreSQL..."
while ! nc -z $DB_HOST $DB_PORT; do
    sleep 0.1
done
echo "PostgreSQL started"

# Wait for Redis
echo "Waiting for Redis..."
while ! nc -z $REDIS_HOST $REDIS_PORT; do
    sleep 0.1
done
echo "Redis started"

# Run database migrations
echo "Running migrations..."
python manage.py migrate --noinput

# Collect static files
echo "Collecting static files..."
python manage.py collectstatic --noinput

# Create cache table
python manage.py createcachetable

# Create superuser if not exists
if [ -n "$DJANGO_SUPERUSER_EMAIL" ] && [ -n "$DJANGO_SUPERUSER_PASSWORD" ]; then
    echo "Creating superuser..."
    python manage.py shell << END
import os
from django.contrib.auth import get_user_model
User = get_user_model()
if not User.objects.filter(email=os.environ['DJANGO_SUPERUSER_EMAIL']).exists():
    User.objects.create_superuser(
        email=os.environ['DJANGO_SUPERUSER_EMAIL'],
        username=os.environ.get('DJANGO_SUPERUSER_USERNAME', 'admin'),
        password=os.environ['DJANGO_SUPERUSER_PASSWORD']
    )
    print("Superuser created successfully!")
else:
    print("Superuser already exists.")
END
fi

# Start Gunicorn
echo "Starting Gunicorn..."
exec gunicorn "$@"

📊 Monitoring and Logging

Docker Logging Configuration:

# docker-compose.monitoring.yml
version: '3.8'

services:
  # Prometheus for metrics
  prometheus:
    image: prom/prometheus:v2.45.0
    container_name: prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    expose:
      - 9090
    ports:
      - "9090:9090"
    networks:
      - monitoring
    restart: unless-stopped

  # Grafana for dashboards
  grafana:
    image: grafana/grafana:10.0.0
    container_name: grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    expose:
      - 3000
    ports:
      - "3000:3000"
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - prometheus

  # Node Exporter for system metrics
  node-exporter:
    image: prom/node-exporter:v1.6.0
    container_name: node-exporter
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    expose:
      - 9100
    networks:
      - monitoring
    restart: unless-stopped

  # cAdvisor for container metrics
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.47.0
    container_name: cadvisor
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    expose:
      - 8080
    ports:
      - "8080:8080"
    networks:
      - monitoring
    restart: unless-stopped

  # Loki for logs
  loki:
    image: grafana/loki:2.8.2
    container_name: loki
    volumes:
      - ./loki/loki-config.yaml:/etc/loki/local-config.yaml:ro
      - loki_data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    expose:
      - 3100
    networks:
      - monitoring
    restart: unless-stopped

  # Promtail for log collection
  promtail:
    image: grafana/promtail:2.8.2
    container_name: promtail
    volumes:
      - ./promtail/promtail-config.yaml:/etc/promtail/config.yaml:ro
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
    command: -config.file=/etc/promtail/config.yaml
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - loki

volumes:
  prometheus_data:
  grafana_data:
  loki_data:

networks:
  monitoring:
    driver: bridge

Prometheus Configuration:

# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets: []

rule_files: []

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'django'
    static_configs:
      - targets: ['web:8000']
    metrics_path: '/metrics/'

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

⚡ Performance Optimization

1. Docker Build Optimization:

# Use .dockerignore effectively
# Use multi-stage builds
# Order commands properly (least changing first)
# Use specific base image tags (not 'latest')

2. Django Gunicorn Configuration:

# gunicorn.conf.py
import multiprocessing

# Server socket
bind = "0.0.0.0:8000"
backlog = 2048

# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2

# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"

# Process naming
proc_name = "django_gunicorn"

# Server mechanics
daemon = False
pidfile = None
umask = 0
user = None
group = None
tmp_upload_dir = None

# SSL (if needed)
# keyfile = "/path/to/key.pem"
# certfile = "/path/to/cert.pem"

# Worker processes
preload_app = True

# Max requests per worker
max_requests = 1000
max_requests_jitter = 50

# Graceful timeout
graceful_timeout = 30

# Worker class for async (if using async views)
# worker_class = "uvicorn.workers.UvicornWorker"

3. Database Connection Pooling:

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME'),
        'USER': config('DB_USER'),
        'PASSWORD': config('DB_PASSWORD'),
        'HOST': config('DB_HOST'),
        'PORT': config('DB_PORT', default=5432),
        'CONN_MAX_AGE': 600,  # 10 minutes
        'OPTIONS': {
            'connect_timeout': 10,
            'keepalives': 1,
            'keepalives_idle': 30,
            'keepalives_interval': 10,
            'keepalives_count': 5,
        },
        'POOL_OPTIONS': {
            'POOL_SIZE': 20,
            'MAX_OVERFLOW': 10,
            'RECYCLE': 3600,
        },
    }
}

🔧 Development Workflow Commands

Common Docker Commands:

# Build and start containers
docker-compose up -d --build

# View logs
docker-compose logs -f web
docker-compose logs -f db
docker-compose logs -f nginx

# Execute commands in container
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py createsuperuser
docker-compose exec db psql -U django_user -d django_db

# Django management commands
docker-compose exec web python manage.py shell
docker-compose exec web python manage.py test
docker-compose exec web python manage.py makemigrations

# Database operations
docker-compose exec db pg_dump -U django_user django_db > backup.sql
docker-compose exec -T db psql -U django_user django_db < backup.sql

# Container management
docker-compose down              # Stop containers
docker-compose down -v          # Stop and remove volumes
docker-compose restart web      # Restart specific service
docker-compose ps               # List containers
docker-compose top              # Show running processes

# Clean up
docker system prune -a          # Remove unused containers, images, networks
docker volume prune             # Remove unused volumes

# Production commands
docker-compose -f docker-compose.prod.yml up -d --build
docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic

Makefile for Automation:

# Makefile
.PHONY: help build up down logs shell migrate test clean

help:
    @echo "Available commands:"
    @echo "  make build        - Build Docker images"
    @echo "  make up          - Start containers"
    @echo "  make down        - Stop containers"
    @echo "  make logs        - View logs"
    @echo "  make shell       - Open Django shell"
    @echo "  make migrate     - Run migrations"
    @echo "  make test        - Run tests"
    @echo "  make clean       - Clean Docker resources"

build:
    docker-compose build

up:
    docker-compose up -d

down:
    docker-compose down

logs:
    docker-compose logs -f

shell:
    docker-compose exec web python manage.py shell

migrate:
    docker-compose exec web python manage.py migrate

makemigrations:
    docker-compose exec web python manage.py makemigrations

test:
    docker-compose exec web python manage.py test

test-coverage:
    docker-compose exec web python -m pytest --cov=. --cov-report=html

clean:
    docker-compose down -v
    docker system prune -af

# Production
prod-up:
    docker-compose -f docker-compose.prod.yml up -d --build

prod-down:
    docker-compose -f docker-compose.prod.yml down

prod-logs:
    docker-compose -f docker-compose.prod.yml logs -f

# Database backup
backup:
    docker-compose exec db pg_dump -U django_user django_db > backup_$(shell date +%Y%m%d_%H%M%S).sql

# Restore database
restore:
    @read -p "Enter backup file name: " file; \
    docker-compose exec -T db psql -U django_user django_db < $$file

🚀 Deployment Strategies

1. Single Server Deployment:

# docker-compose.deploy.yml
version: '3.8'

services:
  web:
    image: your-registry/django-app:${TAG:-latest}
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
    networks:
      - traefik-public
    environment:
      - DATABASE_URL=postgres://...
      - REDIS_URL=redis://...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.django.rule=Host(`yourdomain.com`)"
      - "traefik.http.routers.django.entrypoints=websecure"
      - "traefik.http.routers.django.tls.certresolver=myresolver"

2. Zero-Downtime Deployment Script:

#!/bin/bash
# deploy.sh

set -e

# Load environment
source .env.prod

# Build and push image
docker build -t your-registry/django-app:${COMMIT_SHA} -f Dockerfile.prod .
docker push your-registry/django-app:${COMMIT_SHA}

# Update stack
docker stack deploy -c docker-compose.deploy.yml django-stack

# Wait for health checks
sleep 30

# Check service health
docker service ps django-stack_web --format "{{.CurrentState}}"

# Remove old images
docker image prune -f

3. Docker Swarm Deployment:

# Initialize swarm
docker swarm init

# Deploy stack
docker stack deploy -c docker-compose.swarm.yml django

# Check services
docker service ls

# Scale services
docker service scale django_web=5

# Update service
docker service update --image your-registry/django-app:new-version django_web

🔍 Debugging Dockerized Django

Common Issues and Solutions:

1. Database Connection Issues:

# Check if database is accessible
docker-compose exec web python -c "
import psycopg2
try:
    conn = psycopg2.connect(
        dbname='django_db',
        user='django_user',
        password='django_password',
        host='db',
        port=5432
    )
    print('Database connection successful')
except Exception as e:
    print(f'Database connection failed: {e}')
"

2. Check Container Logs:

# See detailed logs
docker-compose logs --tail=100 web
docker-compose logs --tail=100 db

# Follow logs in real-time
docker-compose logs -f web

# Check specific service
docker-compose exec web python manage.py check --deploy

3. Debug Memory Issues:

# Check container resource usage
docker stats

# Inspect container
docker inspect django_web_1

# Check processes inside container
docker-compose exec web top

# Memory profiling
docker-compose exec web python -m memory_profiler your_script.py

4. Network Issues:

# Check network connectivity
docker-compose exec web ping db
docker-compose exec web curl http://web:8000/health/

# List networks
docker network ls
docker network inspect django_docker_project_default

# Test port exposure
nc -zv localhost 8000

📦 Docker Hub and Registry

Push to Docker Hub:

# Login to Docker Hub
docker login

# Build and tag image
docker build -t yourusername/django-app:latest -t yourusername/django-app:1.0.0 .

# Push to Docker Hub
docker push yourusername/django-app:latest
docker push yourusername/django-app:1.0.0

# Pull in production
docker pull yourusername/django-app:latest

Private Registry Setup:

# Run private registry
docker run -d -p 5000:5000 --name registry registry:2

# Tag and push to private registry
docker tag django-app localhost:5000/django-app:latest
docker push localhost:5000/django-app:latest

# Pull from private registry
docker pull localhost:5000/django-app:latest

🔐 Security Best Practices

1. Docker Security Scanning:

# Scan for vulnerabilities
docker scan your-image:tag

# Use Snyk
docker run -v /var/run/docker.sock:/var/run/docker.sock snyk/snyk:docker --file Dockerfile

# Use Trivy
docker run aquasec/trivy image your-image:tag

2. Security Hardening:

# Use non-root user
RUN adduser --disabled-password --gecos '' django
USER django

# Copy with correct permissions
COPY --chown=django:django . /app

# Use .dockerignore to exclude sensitive files
# Remove unnecessary packages
RUN apt-get purge -y gcc g++ && apt-get autoremove -y

# Use specific versions
FROM python:3.11-slim@sha256:abc123...

# Scan image regularly
# Keep base images updated
# Use multi-stage builds to reduce attack surface

3. Secret Management:

# docker-compose.yml with secrets
services:
  web:
    image: django-app:latest
    secrets:
      - django_secret_key
      - db_password
    environment:
      - SECRET_KEY_FILE=/run/secrets/django_secret_key
      - DB_PASSWORD_FILE=/run/secrets/db_password

secrets:
  django_secret_key:
    file: ./secrets/django_secret_key.txt
  db_password:
    file: ./secrets/db_password.txt

🎯 CI/CD Pipeline Example

GitHub Actions Workflow:

# .github/workflows/docker-ci-cd.yml
name: Django Docker CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v3

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt

    - name: Run migrations
      env:
        DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
        REDIS_URL: redis://redis:6379/0
      run: |
        python manage.py migrate

    - name: Run tests
      env:
        DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
        REDIS_URL: redis://redis:6379/0
        SECRET_KEY: test-secret-key
      run: |
        python manage.py test

    - name: Security scan
      run: |
        pip install bandit safety
        bandit -r .
        safety check -r requirements.txt

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - uses: actions/checkout@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Login to DockerHub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Build and push
      uses: docker/build-push-action@v4
      with:
        context: .
        file: ./Dockerfile.prod
        push: true
        tags: |
          ${{ secrets.DOCKER_USERNAME }}/django-app:latest
          ${{ secrets.DOCKER_USERNAME }}/django-app:${{ github.sha }}
        cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/django-app:buildcache
        cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/django-app:buildcache,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - name: Deploy to production
      uses: appleboy/ssh-action@v0.1.5
      with:
        host: ${{ secrets.PRODUCTION_HOST }}
        username: ${{ secrets.PRODUCTION_USER }}
        key: ${{ secrets.PRODUCTION_SSH_KEY }}
        script: |
          cd /opt/django-app
          docker pull ${{ secrets.DOCKER_USERNAME }}/django-app:latest
          docker-compose -f docker-compose.prod.yml down
          docker-compose -f docker-compose.prod.yml up -d
          docker system prune -af

📚 Cheat Sheet

Docker Commands:

# Image Management
docker build -t myapp .
docker images
docker rmi myapp
docker image prune

# Container Management
docker run -d -p 8000:8000 myapp
docker ps
docker ps -a
docker stop container_id
docker rm container_id
docker exec -it container_id bash

# Docker Compose
docker-compose up -d
docker-compose down
docker-compose logs -f
docker-compose exec service_name command
docker-compose ps
docker-compose restart

# Cleanup
docker system prune -a
docker volume prune
docker network prune

# Monitoring
docker stats
docker top container_id
docker logs container_id

Django Docker Commands:

# Development
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py createsuperuser
docker-compose exec web python manage.py shell
docker-compose exec web python manage.py test

# Production
docker-compose -f docker-compose.prod.yml up -d --build
docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic
docker-compose -f docker-compose.prod.yml logs -f web

# Database
docker-compose exec db psql -U user -d dbname
docker-compose exec db pg_dump -U user dbname > backup.sql

✅ Production Checklist

Before Deployment:

  • Security Scanning: Scan Docker images for vulnerabilities

  • Environment Variables: All sensitive data in env files

  • Database Backups: Backup strategy in place

  • Monitoring: Set up logging and monitoring

  • SSL/TLS: Configure HTTPS

  • Backup Strategy: Database and media files

  • Load Testing: Test under production-like load

  • Disaster Recovery: Plan for failures

After Deployment:

  • Health Checks: Verify all services are healthy

  • Performance: Monitor response times

  • Security: Regular security updates

  • Backups: Verify backup integrity

  • Scaling: Monitor and scale as needed

  • Updates: Regular Docker and Django updates


🚨 Common Pitfalls and Solutions

PitfallSolution
Database not readyUse health checks and wait-for scripts
Permission issuesUse non-root user and correct file permissions
Volume mountingUse named volumes for production data
Memory leaksSet memory limits and monitor usage
Build cache issuesUse multi-stage builds and proper .dockerignore
Slow buildsUse build cache and optimize Dockerfile order
Network issuesUse Docker networks and correct service names
TimeoutsIncrease timeouts in Django and Docker

📈 Performance Metrics to Monitor

# Django metrics endpoint
# urls.py
from django_prometheus.exports import ExportToDjangoView

urlpatterns = [
    path('metrics/', ExportToDjangoView, name='prometheus-django-metrics'),
]

# Monitor:
# - Request latency (p50, p95, p99)
# - Database query performance
# - Cache hit rates
# - Memory usage per worker
# - CPU utilization
# - Queue lengths (Celery)
# - Error rates
# - Response status codes

🎉 Congratulations!

You now have a complete Docker setup for Django/DRF that covers:

Development environment with hot-reload
Production setup with optimization
Multi-container orchestration
Security best practices
Monitoring and logging
CI/CD pipeline
Scaling strategies

Remember: Start simple, iterate, and always test your Docker configurations thoroughly. The power of Docker is that you can evolve your setup as your application grows.

Happy Dockering! 🐳🚀

More from this blog

D

devzodiac

12 posts