Django on AWS Fargate
Table of Contents
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).
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
Container Configuration
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"]
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:
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
}
}
]
}
Load Balancing and Auto-Scaling
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
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
}
}
Database Considerations
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
Django Database Settings
import dj_database_url DATABASES = { 'default': dj_database_url.config( conn_max_age=600, conn_health_checks=True, ssl_require=True ) }
Environment Configuration
Settings Structure
Organize settings for different environments:
myproject/
settings/
__init__.py
base.py
development.py
production.py
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', }, }
Deployment Process
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
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"]}]}'
Step 3: Update ECS Service
aws ecs update-service \ --cluster django-cluster \ --service django-service \ --force-new-deployment
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
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
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
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.