How to Deploy Django with 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 definitionnginx.conf- Web server configurationcompose.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:
- ✓ Configure environment variables in
.env - ✓ Start with HTTP-only Nginx configuration
- ✓ Obtain SSL certificates with Certbot
- ✓ Update Nginx to enable HTTPS
- ✓ Enable automatic certificate renewal
- ✓ Monitor logs and verify all services are running
Your Django application is now deployed with Docker, secured with HTTPS, and ready for production! 🚀