Nginx Deployment Configuration
Overview
The AV project now includes an internal Nginx service that serves as a reverse proxy and static file server. This improves performance and follows production best practices.
Architecture
Client Requests
↓
┌─────────────────────────┐
│ Nginx (Port 80) │
│ │
│ - Reverse Proxy │
│ - Static Files Cache │
│ - Compression (Gzip) │
└────────┬────────────────┘
├─→ /static/ → static_volume (7-day cache)
├─→ /media/ → media_volume (1-day cache)
├─→ /docs/ → site/ (7-day cache)
└─→ /* → Django Web App (8000)
Services
Nginx Container
- Image:
nginx:1.27-alpine - Port:
80(external) - Container Name:
av_nginx - Dependencies: Waits for
webservice to be healthy
Configuration Files
docker/nginx/Dockerfile
Lightweight alpine-based nginx image with health checks:
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
HEALTHCHECK --interval=30s --timeout=3s ...
docker/nginx/nginx.conf
Main nginx configuration with:
- Worker processes: Auto-detect (scalable)
- Client max body size: 100MB
- Gzip compression: Enabled for text/JSON/JS assets
- Static file caching: 7 days for /static/, 1 day for /media/
- Proxy settings: Passes requests to Django with proper headers
- Health endpoint: /health for container health checks
File Serving
Static Files (/static/)
- Source:
static_volume:/app/staticfiles - Cache: 7 days
- Use Cases: CSS, JS, admin assets
- Served Directly: No Django processing needed
Media Files (/media/)
- Source:
media_volume:/app/media - Cache: 1 day
- Use Cases: User-uploaded files, images
- Read-Only: Mounted as
ro(read-only)
Documentation (/docs/)
- Source:
./site(MkDocs generated HTML) - Cache: 7 days
- Fallback: Serves
index.htmlfor SPA-like behavior
Request Routing
| Path | Handler | Caching | Notes |
|---|---|---|---|
/static/* |
Nginx | 7 days | CSS, JS, images |
/media/* |
Nginx | 1 day | User uploads |
/docs/* |
Nginx | 7 days | Documentation |
/* |
Django | None | API endpoints, views |
Environment Variables
All existing environment variables work as before. The Django app still runs on port 8000 internally.
For external access:
- API: http://localhost/api/ (proxied through nginx)
- Docs: http://localhost/docs/ (served by nginx)
- Admin: http://localhost/admin/ (proxied through nginx)
- Django Dev: http://localhost:8000/ (direct access, bypasses nginx)
Development vs Production
Development (docker compose up)
Port 80 (Nginx) ────→ Static files + Reverse proxy
Port 8000 (Django) ──→ Direct access for debugging
Production Deployment
For production, update the Django service:
# In docker-compose.yml for production
web:
# ...
ports: [] # Remove port 8000 exposure
command: >
sh -c "
uv run python manage.py migrate &&
uv run python manage.py collectstatic --noinput &&
gunicorn --bind 0.0.0.0:8000 core.wsgi
"
And update environment:
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
CORS_ALLOW_ALL_ORIGINS=False
CORS_ALLOWED_ORIGINS=https://yourdomain.com
Nginx Performance Features
Gzip Compression
Enabled for: - Plain text - CSS, XML, JavaScript - JSON - Fonts (TTF, OTF) - SVG images
Reduces bandwidth by ~70-80% for text-based assets.
Caching Headers
/static/ → Cache-Control: public, immutable (7 days)
/media/ → Cache-Control: public (1 day)
/docs/ → Cache-Control: public (7 days)
Connection Optimization
sendfile on: Zero-copy file transmissiontcp_nopush on: Optimize TCP packet transmissionkeepalive_timeout 65: Connection reuse
Health Checks
Both services have health checks:
Nginx:
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 30s
timeout: 3s
retries: 3
Django (unchanged):
Logs
View nginx logs:
Common log locations in container:
- Access logs: /var/log/nginx/access.log
- Error logs: /var/log/nginx/error.log
Troubleshooting
Nginx container won't start
Static files not loading
# Verify volumes are mounted
docker inspect av_nginx | grep -A 5 Mounts
# Check file permissions
docker exec av_nginx ls -la /app/staticfiles/
Slow responses
# Check nginx logs for upstream errors
docker logs av_nginx | tail -f
# Monitor nginx performance
docker stats av_nginx
Port 80 already in use
# Change nginx port in docker-compose.yml
ports:
- "8080:80" # Maps container:80 to host:8080
# Access at http://localhost:8080/
Advanced Configuration
Add HTTPS (with Let's Encrypt)
Update nginx.conf:
server {
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# ... rest of config
}
Add Rate Limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://django;
}
Custom Error Pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app/site;
}