Deploying Django Like a Pro: Production-Ready Pipeline with Docker, Nginx, and EC2

By Dawood Ahmed | Published on April 20, 2025
Deploying Django Like a Pro: Production-Ready Pipeline with Docker, Nginx, and EC2

From Zero to Live – A complete case study walkthrough of a modern production-ready Django deployment pipeline.

πŸš€ Introduction

If you're a developer or freelancer deploying Django projects for clients or yourself, this guide shows how to achieve a production-grade, secure, and scalable deployment on a tight budget. We'll use an EC2 instance, Docker, Nginx, Gunicorn, Certbot, and PostgreSQL.

Use Case: Perfect for personal portfolios, client projects, or scalable MVPs.

πŸ”Œ Step 1: Connect to Your EC2 Server

SSH into your EC2 instance using your key:

ssh -i path-to-key.pem ec2-user@your-ec2-ip

Make sure your domain points to your EC2's public IP via A record in your domain DNS settings.

πŸ“ Step 2: Project Directory Structure

project-root/
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ nginx/
β”‚   └── default.conf
β”œβ”€β”€ certbot/
β”‚   β”œβ”€β”€ www/
β”‚   └── conf/
β”œβ”€β”€ .env
└── portfolio/  # Django project folder

🐳 Step 3: Create Dockerfile for Django

FROM python:3.10-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "portfolio.wsgi:application", "--bind", "0.0.0.0:8000"]

🌐 Step 4: Setup HTTP with Nginx

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

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

  location / {
    proxy_pass http://portfolio-web:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

πŸ“¦ Step 5: Docker Compose for Services

version: "3.9"
services:
  portfolio-web:
    build: .
    volumes:
      - .:/app
    expose:
      - "8000"
    env_file:
      - .env

  nginx-container:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt
    depends_on:
      - portfolio-web

πŸ”’ Step 6: Stop Services & Get SSL

Stop containers to free port 80:

docker-compose down

Then run Certbot:

docker run -it --rm \
  -v "$(pwd)/certbot/www:/var/www/certbot" \
  -v "$(pwd)/certbot/conf:/etc/letsencrypt" \
  certbot/certbot certonly \
  --webroot --webroot-path=/var/www/certbot \
  --email your-email@example.com \
  --agree-tos --no-eff-email \
  -d your-domain.com -d www.your-domain.com

πŸ” Step 7: Add HTTPS Support in Nginx

server {
  listen 80;
  server_name your-domain.com www.your-domain.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  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;

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

  location / {
    proxy_pass http://portfolio-web:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
}

♻️ Step 8: Update Compose for Auto Renewal

certbot:
  image: certbot/certbot
  volumes:
    - ./certbot/www:/var/www/certbot
    - ./certbot/conf:/etc/letsencrypt
  entrypoint: /bin/sh -c
  command: "trap exit TERM; while :; do sleep 6h & wait $${!}; certbot renew; done"

βœ… Final Launch

docker-compose up -d --build

πŸ’‘ Bonus: Validate SSL Renewal

docker-compose run --rm certbot renew --dry-run

Written by an AI + Web Dev + Automation Expert πŸš€ | Looking to build or deploy your project? Hire Me

Share this Post:

Leave a comment

Latest Comments

No comments yet. Be the first to share your thoughts!