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:
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";
}
}
5. Deployment & Debugging Workflow
Deployment Steps1. Launch Containers: docker compose up -d --build
-
Initialize Database:
docker compose exec web python manage.py migrate -
Sync Static Files:
docker compose exec web python manage.py collectstatic --noinput -
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
-
DoesNotExistError: You likely forgot to create required objects (like aPersonaorSite) in the new production database. Create them via the Admin panel. -
Connection Refused: Check if thedborrediscontainers are actually running.
II. Checking Nginx (The 502/404 Debug)
If you see a 502 Bad Gateway:
- The
webcontainer has crashed. Checkdocker compose psto 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_volumeis not mounted correctly in thenginxservice of yourdocker-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
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)