Django on AWS Fargate
Table of Contents
1. Introduction
AWS Fargate provides a serverless compute engine for containers, eliminating the need to manage EC2 instances. This guide covers deploying Django applications on AWS Fargate using Amazon ECS (Elastic Container Service).
2. Architecture Overview
A typical Django on Fargate architecture consists of:
- Application Load Balancer (ALB) for request distribution
- ECS Service running Fargate tasks
- Amazon RDS for PostgreSQL/MySQL database
- Amazon ElastiCache for Redis (caching and sessions)
- Amazon S3 for static and media files
- AWS Secrets Manager for sensitive configuration
3. Container Configuration
3.1. Dockerfile
Create a production-ready Dockerfile:
FROM python:3.11-slim
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
# Install system dependencies
RUN apt-get update && apt-get install -y \
libpq-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Create application directory
WORKDIR /app
# Install Python dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt gunicorn
# Copy application code
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
# Create non-root user
RUN useradd -m -u 1000 django && \
chown -R django:django /app
USER django
# Expose port
EXPOSE 8000
# Run gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "myproject.wsgi:application"]
3.2. Docker Compose for Local Development
version: '3.8'
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:
4. ECS Task Definition
Create a task definition JSON file:
{
"family": "django-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "django-web",
"image": "ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/django-app:latest",
"essential": true,
"portMappings": [
{
"containerPort": 8000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "DJANGO_SETTINGS_MODULE",
"value": "myproject.settings.production"
},
{
"name": "AWS_STORAGE_BUCKET_NAME",
"value": "my-django-static-files"
}
],
"secrets": [
{
"name": "SECRET_KEY",
"valueFrom": "arn:aws:secretsmanager:REGION:ACCOUNT_ID:secret:django/SECRET_KEY"
},
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:REGION:ACCOUNT_ID:secret:django/DATABASE_URL"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/django-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8000/health/ || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
5. Load Balancing and Auto-Scaling
5.1. Application Load Balancer Configuration
The ALB should be configured with:
- HTTPS listener (port 443) with ACM certificate
- HTTP listener (port 80) redirecting to HTTPS
- Target group for ECS service with health checks
- Security group allowing inbound traffic on ports 80/443
5.2. Auto-Scaling Policy
{
"ServiceName": "django-service",
"ScalableDimension": "ecs:service:DesiredCount",
"MinCapacity": 2,
"MaxCapacity": 10,
"TargetTrackingScalingPolicyConfiguration": {
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleInCooldown": 300,
"ScaleOutCooldown": 60
}
}
6. Database Considerations
6.1. RDS Configuration
For production Django deployments:
- Use Amazon RDS for PostgreSQL or MySQL
- Enable Multi-AZ for high availability
- Configure automated backups with appropriate retention
- Use db.t3.medium or larger for production workloads
- Place in private subnets
- Configure security groups to allow only ECS tasks access
6.2. Django Database Settings
import dj_database_url
DATABASES = {
'default': dj_database_url.config(
conn_max_age=600,
conn_health_checks=True,
ssl_require=True
)
}
7. Environment Configuration
7.1. Settings Structure
Organize settings for different environments:
myproject/
settings/
__init__.py
base.py
development.py
production.py
7.2. Production Settings
from .base import *
import os
DEBUG = False
ALLOWED_HOSTS = [os.environ.get('ALLOWED_HOST', '.amazonaws.com')]
# Security settings
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
# Static and media files with S3
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'
# Cache configuration with ElastiCache
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': os.environ['REDIS_URL'],
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'INFO',
},
}
8. Deployment Process
8.1. Step 1: Build and Push Docker Image
# Authenticate with ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com
# Build image
docker build -t django-app .
# Tag image
docker tag django-app:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/django-app:latest
# Push to ECR
docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/django-app:latest
8.2. Step 2: Run Database Migrations
Use ECS Run Task to execute one-off commands:
aws ecs run-task \
--cluster django-cluster \
--task-definition django-migrate \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx]}" \
--overrides '{"containerOverrides":[{"name":"django-web","command":["python","manage.py","migrate"]}]}'
8.3. Step 3: Update ECS Service
aws ecs update-service \
--cluster django-cluster \
--service django-service \
--force-new-deployment
9. Monitoring and Logging
- Use CloudWatch Logs for application logs
- Set up CloudWatch Alarms for CPU, memory, and error rates
- Configure X-Ray for distributed tracing
- Use CloudWatch Container Insights for detailed metrics
- Implement health check endpoints in Django
10. Cost Optimization
- Use Fargate Spot for non-critical workloads
- Right-size CPU and memory allocations
- Implement auto-scaling to match demand
- Use RDS Reserved Instances for predictable workloads
- Configure S3 lifecycle policies for old static files
11. Security Best Practices
- Use AWS Secrets Manager for sensitive data
- Implement least-privilege IAM roles
- Enable VPC Flow Logs
- Use AWS WAF with ALB
- Regularly update base Docker images
- Scan images with Amazon ECR scanning
12. Conclusion
Deploying Django on AWS Fargate provides a scalable, serverless container platform that reduces operational overhead while maintaining flexibility. By following these practices, you can build a production-ready Django deployment that scales with your application needs.