DEV Community

Cover image for Production Deployment: Django, Docker Compose, & Nginx on AWS
Ajit Kumar
Ajit Kumar

Posted on

Production Deployment: Django, Docker Compose, & Nginx on AWS

This guide outlines the architecture and execution steps for a containerized Django stack.

1. Hardware & Infrastructure (AWS EC2)

Instance SelectionFor this multi-container stack, a t3.small (2GB RAM) is the absolute minimum requirement. Using a smaller instance (like a t3.micro) will cause the database and Celery workers to crash due to "Out of Memory" (OOM) errors during heavy tasks or builds.

Security Group (Firewall)| Type | Port | Source | Description |
| --- | --- | --- | --- |
| SSH | 22 | My IP | Secure terminal access |
| HTTP | 80 | 0.0.0.0/0 | Public web traffic |
| Custom | 5555 | My IP | Flower (Celery monitoring) |


2. Orchestration:

The Full docker-compose.ymlThis configuration ensures that all components—from the database to the task workers—are linked and share the necessary volumes for static files and data persistence.

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.web
    volumes:
      - .:/app
      - static_volume:/app/staticfiles
    ports:
      - "8000:8000"
    env_file: .env
    command: ["daphne", "-b", "0.0.0.0", "-p", "8000", "config.asgi:application"]
    depends_on:
      - db
      - redis

  db:
    image: postgres:17
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - "POSTGRES_HOST_AUTH_METHOD=trust"

  redis:
    image: redis:latest
    volumes:
      - redis_data:/data

  celery:
    build:
      context: .
      dockerfile: Dockerfile.worker
    volumes:
      - .:/app
    env_file: .env
    depends_on:
      - redis
      - db

  beat:
    build:
      context: .
      dockerfile: Dockerfile.beat
    volumes:
      - .:/app
    env_file: .env
    depends_on:
      - celery
      - redis

  flower:
    build:
      context: .
      dockerfile: Dockerfile.worker
    command: celery -A config.celery:app flower --port=5555
    ports:
      - 5555:5555
    env_file: .env
    depends_on:
      - redis

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - static_volume:/app/staticfiles:ro
    depends_on:
      - web

volumes:
  postgres_data:
  redis_data:
  static_volume:

Enter fullscreen mode Exit fullscreen mode

3. Environment Variables & Security

Docker uses the $ symbol for variable substitution. If your SECRET_KEY or DATABASE_URL contains a $, Docker will remove it and everything following it, leading to corrupted strings.

The Fix: Escape every $ with $$.

  • Incorrect: SECRET_KEY=my$secret
  • Correct: SECRET_KEY=my$$secret

4. Reverse Proxy: nginx.conf

Nginx handles incoming requests. It serves static assets directly from the static_volume and proxies application logic to the web container.

upstream django_app {
    server web:8000;
}

server {
    listen 80;
    server_name your_ec2_public_ip;

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

    location / {
        proxy_pass http://django_app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Enter fullscreen mode Exit fullscreen mode

5. Deployment & Debugging Workflow

Deployment Steps1. Launch Containers: docker compose up -d --build

  1. Initialize Database: docker compose exec web python manage.py migrate
  2. Sync Static Files: docker compose exec web python manage.py collectstatic --noinput
  3. Create Admin: docker compose exec web python manage.py createsuperuser

Deep-Dive DebuggingWhen the application fails, use the following logic to isolate the issue:

I. Checking Logs (The "Watch" Command)

If the browser shows a 500 Error, watch the Django logs in real-time:

docker compose logs web -f

Enter fullscreen mode Exit fullscreen mode
  • DoesNotExist Error: You likely forgot to create required objects (like a Persona or Site) in the new production database. Create them via the Admin panel.
  • Connection Refused: Check if the db or redis containers are actually running.

II. Checking Nginx (The 502/404 Debug)

If you see a 502 Bad Gateway:

  • The web container has crashed. Check docker compose ps to see if it is in an "Exited" state.

If the site loads but has No CSS:

  • Run: docker compose exec nginx ls -l /app/staticfiles.
  • If this returns an error, your static_volume is not mounted correctly in the nginx service of your docker-compose.yml.

III. Verifying Workers

If background tasks (like emails or AI processing) aren't happening, check the Celery logs:

docker compose logs celery -f

Enter fullscreen mode Exit fullscreen mode

6. Summary of Solutions

Symptom Probable Cause Fix
Hanging SSH Security Group Port 22 closed Open Port 22 for your IP in AWS Console
500 Error Missing DB records Use createsuperuser and add data via /admin
Password Fail Corrupted Secret Key Check .env for unescaped $ signs
Blank CSS Volume Mismatch Ensure static_volume is shared by both web and nginx

Top comments (0)