$ matvix90.blog

> A minimal command-line inspired blog

How to Deploy Django with Docker

$ cat 2025-12-07.txt
deploy django docker

Deploying Django applications with Docker provides a consistent, scalable, and production-ready environment. In this guide, I'll show you how to containerize your Django app with Nginx, PostgreSQL, and automatic SSL certificate management.

Why Docker for Django Deployment?

Docker-based deployment offers several advantages:

  • Consistency - Same environment from development to production
  • Isolation - Each service runs in its own container
  • Scalability - Easy to scale horizontally with container orchestration
  • Simplicity - One command to deploy your entire stack

Project Structure

A minimal Django project ready for Docker deployment includes:

  • Dockerfile - Django application container definition
  • nginx.conf - Web server configuration
  • compose.yml - Multi-container orchestration
  • .env.example - Environment variables template

Production Dockerfile

Create a production-ready Dockerfile for your Django app:

FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

RUN apt-get update && apt-get install -y \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN python manage.py collectstatic --noinput

EXPOSE 8000

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

Initial Nginx Configuration (HTTP Only)

Start with HTTP-only configuration before setting up SSL:

upstream django {
    server web:8000;
}

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location /static/ {
        alias /app/staticfiles/;
    }

    location /media/ {
        alias /app/mediafiles/;
    }

    location / {
        proxy_pass http://django;
        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;
    }
}

Docker Compose Configuration

Create a compose.yml file with all required services:

services:
  db:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    restart: unless-stopped

  web:
    build: .
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - DEBUG=False
      - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    depends_on:
      - db
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - static_volume:/app/staticfiles:ro
      - media_volume:/app/mediafiles:ro
      - certbot_conf:/etc/letsencrypt
      - certbot_www:/var/www/certbot
    depends_on:
      - web
    restart: unless-stopped

  certbot:
    image: certbot/certbot
    volumes:
      - certbot_conf:/etc/letsencrypt
      - certbot_www:/var/www/certbot
    # Uncomment after initial certificate setup:
    # entrypoint: /bin/sh -c "trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet --deploy-hook 'nginx -s reload' || true; sleep 12h & wait $${!}; done;"

volumes:
  postgres_data:
  static_volume:
  media_volume:
  certbot_conf:
  certbot_www:

Manual Deployment

Initial Setup

Get your application running with these commands:

# Copy example environment file
cp .env.example .env

# Edit environment variables
nano .env

# Build and start services
docker compose up -d

# Check service status
docker compose ps

# View logs
docker compose logs -f

# Create Django superuser
docker compose exec web python manage.py createsuperuser

SSL Certificate Setup

Now let's secure your site with HTTPS using Let's Encrypt.

Prepare Certbot Directory:

docker compose exec nginx mkdir -p /var/www/certbot/.well-known/acme-challenge
docker compose exec nginx chmod -R 755 /var/www/certbot

Verify ACME Challenge Access:

# Create a test file
docker compose exec nginx sh -c "echo 'test123' > /var/www/certbot/.well-known/acme-challenge/test"

# Try to access it from the internet
curl http://DOMAIN/.well-known/acme-challenge/test

Obtain Certificate (Staging):

Test the certificate process first to avoid rate limits:

docker compose run --rm certbot \
  certonly --webroot -w /var/www/certbot \
  --email your@email.com \
  --agree-tos \
  --no-eff-email \
  --staging \
  -d DOMAIN \
  -d www.DOMAIN

Obtain Certificate (Production):

Once staging works, get the real certificate:

docker compose run --rm certbot \
  certonly --webroot -w /var/www/certbot \
  --email your@email.com \
  --agree-tos \
  --no-eff-email \
  -d DOMAIN \
  -d www.DOMAIN

Verify Certificates:

docker compose run --rm certbot certificates

Enable HTTPS in Nginx

Update your nginx.conf to include HTTPS configuration:

upstream django {
    server web:8000;
}

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location /static/ {
        alias /app/staticfiles/;
    }

    location /media/ {
        alias /app/mediafiles/;
    }

    location / {
        proxy_pass http://django;
        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;
    }
}

Reload Nginx to apply changes:

docker compose exec nginx nginx -s reload

Enable Certbot Auto-Renewal

Edit your compose.yml to uncomment the certbot entrypoint:

certbot:
  image: certbot/certbot
  volumes:
    - certbot_conf:/etc/letsencrypt
    - certbot_www:/var/www/certbot
  # Uncomment this line:
  entrypoint: /bin/sh -c "trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet --deploy-hook 'nginx -s reload' || true; sleep 12h & wait $${!}; done;"

Restart the certbot service:

docker compose up -d certbot

Check Renewal Process

Verify automatic renewal is working:

# Check certificate status
docker compose run --rm --entrypoint "certbot" certbot certificates 2>/dev/null | grep -A 6 "Certificate Name"

# Test renewal process (dry run)
docker compose run --rm --entrypoint "sh" certbot -c "apk add --no-cache docker-cli > /dev/null 2>&1 && certbot renew --webroot -w /var/www/certbot --dry-run --deploy-hook 'echo \"✅ Deploy hook test passed\"'" 2>&1 | tail -5

Rebuild and Restart Containers

When updating your application:

# Stop services
docker compose down

# Rebuild and start services
docker compose up --build -d

Key Takeaways

Docker simplifies Django deployment by containerizing your application, database, web server, and SSL management into a single orchestrated system.

Deployment checklist:

  1. ✓ Configure environment variables in .env
  2. ✓ Start with HTTP-only Nginx configuration
  3. ✓ Obtain SSL certificates with Certbot
  4. ✓ Update Nginx to enable HTTPS
  5. ✓ Enable automatic certificate renewal
  6. ✓ Monitor logs and verify all services are running

Your Django application is now deployed with Docker, secured with HTTPS, and ready for production! 🚀