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.

Author: Jason Walsh

j@wal.sh

Last Updated: 2025-12-22 21:33:32

build: 2025-12-29 20:01 | sha: 34015db