Guide to Deploying a Scalable Django + DRF App on AWS with Docker, ECS, and Fargate

Search for a command to run...

No comments yet. Be the first to comment.
There's a question that most people feel but almost nobody says out loud. Not because it's complicated. Because saying it threatens everything built on top of not saying it. The question is simple. Wh

Public discourse around personal finance in India increasingly relies on lifestyle narratives, absolute numbers, and loosely imported benchmarks. Concepts such as middle class, financial security, high income, or wealthy are often used without refere...

By Ahmad W Khan (interactive at https://ahmadwkhan.com/india-work-landscape) Summary (TL;DR): Indiaβs labour market is vast, diversified, and uneven. Government roles remain tenureβrich but hard to enter; healthcare and licensed professional practice...

Audience: Intermediate PHP devs (comfortable with OOP, Composer, basic MVC) who are new/rusty with SymfonyOS Assumptions: macOS/Linux primary; Windows notes included (PowerShell + WSL2)Target PHP & Symfony: PHP 8.2+ and Symfony 7.3.x (current stable ...

Money, unlike geography, is invisible.We know where a country starts and ends on a map. But wealth and income? Theyβre like air currents, everywhere, yet hard to see. Percentiles help make them visible: where do you sit compared to your neighbors, yo...

Deploying a Django + DRF app in production isnβt as straightforward as running python manage.py runserver. For local development, Django's built-in development server works well enough, but it's not designed to handle high traffic, scale across multiple servers, or integrate with complex cloud infrastructure.
To deploy a production-ready Django app that is scalable, secure, and highly available, we need a more robust infrastructure. This is where Docker, AWS ECS, Fargate, and Kubernetes come in.
In this guide, we will create a containerized Django application, push it to a container registry (AWS ECR), deploy it on AWS using ECS with Fargate, and set up automatic scaling, load balancing, and monitoring. We'll also secure the app with HTTPS using AWS ACM (Certificate Manager), and store static/media files in AWS S3. Finally, weβll explore how Kubernetes fits into the picture and why you may want to use it over ECS for complex setups.
If you follow along, you'll learn how to:
Dockerize a Django app
Create an AWS ECR repository
Deploy the app on AWS ECS using Fargate
Configure load balancing using AWS ALB
Set up auto-scaling
Store static and media files on AWS S3
Manage secrets securely using AWS Secrets Manager
Set up monitoring using AWS CloudWatch
Secure the app with HTTPS using AWS ACM
Optionally deploy using Kubernetes
Before we begin, ensure that you have the following:
Basic understanding of Docker
Familiarity with Django and DRF
AWS account with admin access
AWS CLI installed
Docker installed
| Tool | Version | Purpose |
| Python | 3.11+ | Programming language |
| Django | 4.2+ | Web framework |
| DRF | 3.14+ | REST API framework |
| Docker | Latest | Containerization |
| AWS CLI | Latest | AWS command-line interface |
| Gunicorn | Latest | Production WSGI server |
| Postgres | 14+ | Database |
| Kubernetes (Optional) | Latest | Container orchestration |
Letβs begin by creating a new Django project and setting up DRF.
Create a project directory and a virtual environment:
mkdir django-aws-deploy
cd django-aws-deploy
python -m venv env
source env/bin/activate
Install Django, DRF, and Gunicorn:
pip install django djangorestframework gunicorn psycopg2-binary
Create a new Django project:
django-admin startproject myproject .
Create a DRF app:
python manage.py startapp api
Add rest_framework to INSTALLED_APPS in settings.py:
myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api',
]
api/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def hello_world(request):
return Response({'message': 'Hello World!'})
api/urls.py
from django.urls import path
from .views import hello_world
urlpatterns = [
path('hello/', hello_world),
]
myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
Test the endpoint:
python manage.py runserver
Navigate to:
http://127.0.0.1:8000/api/hello/
To ensure our app runs consistently across different environments, we need to containerize it using Docker.
Create a Dockerfile in the root of the project:
Dockerfile
# Use official Python image
FROM python:3.11-slim
# Environment settings
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set working directory
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy project files
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
# Expose port
EXPOSE 8000
# Start server with Gunicorn
CMD ["gunicorn", "myproject.wsgi", "--bind", "0.0.0.0:8000", "--workers", "4"]
docker-compose.yml
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
env_file:
- .env
volumes:
- .:/app
depends_on:
- db
db:
image: postgres:14
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
.dockerignore FileExclude unnecessary files from Docker builds:
.dockerignore
__pycache__
*.pyc
*.log
venv
node_modules
.env
Build the Docker container:
docker-compose build
Run the container:
docker-compose up
Stop the container:
docker-compose down
Check running containers:
docker ps
Get logs:
docker logs <container-name>
Open a shell into the running container:
docker exec -it <container-name> bash
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com
docker tag myproject:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/myproject:latest
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/myproject:latest
Elastic Container Service (ECS) is Amazonβs native container orchestration service. It's similar to Kubernetes but tightly integrated with AWS infrastructure, making it easier to manage and scale.
Fargate allows you to run ECS containers without managing EC2 instances or infrastructure.
No need to provision EC2 instances
Fully managed by AWS
Scales automatically based on demand
Secure by default using IAM and VPC
Create an ECS cluster using Fargate.
Create a task definition to define the container settings.
Create an ECS service to run the container.
Set up an Application Load Balancer (ALB) for traffic routing.
Set up auto-scaling based on traffic.
Configure AWS Secrets Manager for securely handling sensitive information.
Set up logging and monitoring using CloudWatch.
An ECS Cluster is the foundational unit for deploying Docker containers on AWS.
Fargate will handle the underlying infrastructure.
We'll create a cluster that allows containers to scale based on load.
Open the AWS Management Console.
Go to Elastic Container Service (ECS) β Clusters β Create Cluster.
Select "Networking Only" (Fargate).
Name the cluster β django-cluster.
Create the cluster.
Check that the cluster is created:
aws ecs list-clusters
A task definition is a blueprint that defines how a container should run in ECS:
Which Docker image to use
Memory and CPU limits
Logging configuration
Network mode
Go to ECS β Task Definitions β Create Task Definition.
Choose Fargate.
Set the following:
Task Name: django-task
Task Role: Create a new role (ecsTaskExecutionRole)
Network Mode: awsvpc
CPU: 512
Memory: 1024
Add a container to the task definition:
Container Name: django-app
Image: account-id.dkr.ecr.us-east-1.amazonaws.com/myproject:latest
Port Mappings: 8000
Enable logging to CloudWatch:
Log Driver: awslogs
Log Group: /ecs/django-app
Region: us-east-1
Stream Prefix: ecs
Pass environment variables to the container:
| Key | Value |
DEBUG | False |
ALLOWED_HOSTS | * |
DATABASE_URL | Retrieved from Secrets Manager |
SECRET_KEY | Retrieved from Secrets Manager |
Add a health check:
Protocol: HTTP
Path: /health/
Interval: 30 seconds
Timeout: 5 seconds
Healthy Threshold: 2
Unhealthy Threshold: 2
Save the task definition.
An ECS Service allows you to run and maintain a specified number of instances of a task definition.
Go to ECS β Create Service
Select "Fargate" as the launch type.
Cluster: django-cluster
Service Type: Replica
Number of tasks: 2 (for high availability)
Choose an existing VPC.
Select at least two subnets in different Availability Zones.
Create a new Security Group:
Allow inbound traffic on port 80 (HTTP)
Allow inbound traffic on port 443 (HTTPS)
Allow inbound traffic on port 8000 (from Load Balancer)
Enable auto-scaling.
Set up a policy based on CPU utilization:
Scale up at 70% CPU
Scale down at 30% CPU
Minimum tasks = 2
Maximum tasks = 10
Save the ECS service.
An ALB will distribute incoming traffic to the ECS tasks.
Go to EC2 β Load Balancers β Create Load Balancer
Choose Application Load Balancer
Scheme: Internet-facing
Security Group: Use the ECS security group
Go to Target Groups
Create a target group for HTTP traffic:
Protocol: HTTP
Port: 8000
Register your ECS tasks in the target group.
Go to Listeners β Add listener
Protocol: HTTP
Forward traffic to the target group
Find the ALB's DNS name:
aws elbv2 describe-load-balancers --query "LoadBalancers[].DNSName"
Test the endpoint:
curl http://<ALB-DNS-Name>/api/hello/
If everything is set up correctly, you'll see:
{"message": "Hello World!"}
AWS ACM (Certificate Manager) provides free SSL certificates.
Go to ACM β Request Certificate
Use DNS validation
Attach to ALB
Go to ALB β Listeners
Add listener for HTTPS (443)
Forward traffic to the ECS target group
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Add a health check for ECS:
api/views.py
@api_view(['GET'])
def health(request):
return Response({"status": "healthy"})
api/urls.py
urlpatterns = [
path('health/', health),
]
After making these changes, restart the ECS service:
aws ecs update-service --cluster django-cluster --service django-service --force-new-deployment
The architecture we'll build will look like this:
+---------------+
| Route 53 |
+---------------+
|
+---------------+
| AWS ACM | <-- SSL Certificate
+---------------+
|
+--------------------------------+
| AWS Application Load Balancer |
+--------------------------------+
| |
+---------------------+ +---------------------+ +---------------------+
| ECS Task | | ECS Task | | ECS Task |
| (Gunicorn) | | (Gunicorn) | | (Gunicorn) |
+---------------------+ +---------------------+ +---------------------+
| | |
+----------------------+ +----------------------+ +----------------------+
| AWS Fargate | | AWS Fargate | | AWS Fargate |
+----------------------+ +----------------------+ +----------------------+
| | |
+----------------------+ +----------------------+ +----------------------+
| AWS VPC | | AWS VPC | | AWS VPC |
+----------------------+ +----------------------+ +----------------------+
| | |
+-----------------------+
| AWS RDS (Postgres) |
+-----------------------+
|
+-----------------------+
| AWS S3 (Static) |
+-----------------------+
|
+-----------------------+
| AWS Secrets Manager |
+-----------------------+
Amazon ECS (Elastic Container Service) allows you to run Docker containers at scale. Fargate lets you run ECS containers without provisioning or managing EC2 instances β AWS manages the infrastructure for you.
Go to ECS β Clusters β Create Cluster
Choose "Networking Only (Fargate)"
Name the cluster β django-cluster
Create the cluster
A task definition is a blueprint for running containers in ECS.
Go to ECS β Task Definitions β Create New
Launch Type β Fargate
Network Mode β awsvpc
Task Size
CPU: 512 (0.5 vCPU)
Memory: 1024 MB (1 GB)
| Parameter | Value |
| Container Name | django-app |
| Image | account-id.dkr.ecr.us-east-1.amazonaws.com/myproject:latest |
| Port | 8000 |
| Essential | Yes |
Configure logs to be sent to CloudWatch:
Log Driver β awslogs
Log Group β /ecs/django-app
Region β us-east-1
Stream Prefix β ecs
| Key | Value |
DEBUG | False |
ALLOWED_HOSTS | * |
DATABASE_URL | Retrieved from Secrets Manager |
SECRET_KEY | Retrieved from Secrets Manager |
Go to ECS β Create Service
Choose:
Launch Type: Fargate
Cluster: django-cluster
Service Type: Replica
Number of tasks: 2 (for high availability)
Networking:
VPC β Select existing VPC
Subnets β Select at least two subnets in different Availability Zones
Security Group β Create new Security Group
Health Check Grace Period: 60 seconds
Enable Auto-Scaling:
Minimum tasks = 2
Maximum tasks = 10
Scale at 70% CPU
Scale down at 30% CPU
Create a new security group for ECS:
Inbound Rules:
Allow HTTP (80) from 0.0.0.0/0
Allow HTTPS (443) from 0.0.0.0/0
Allow port 8000 from Load Balancer
Outbound Rules:
Go to EC2 β Load Balancers
Create a new Application Load Balancer
Type: Internet-facing
Security Group: Use the ECS security group
Target Group: Create a target group for port 8000
Protocol β HTTP
Path β /health/
Interval β 30 seconds
Timeout β 5 seconds
Unhealthy threshold β 2
Healthy threshold β 2
Go to ECS β Services
Edit the service β Add load balancer
Attach to target group
api/views.py
@api_view(['GET'])
def health(request):
return Response({'status': 'healthy'})
api/urls.py
urlpatterns = [
path('health/', health),
]
ALLOWED_HOSTS = ['my-load-balancer-url.us-east-1.elb.amazonaws.com']
aws ecs update-service --cluster django-cluster --service django-service --force-new-deployment
aws logs tail /ecs/django-app --follow
AWS ECS Auto Scaling allows scaling based on CloudWatch metrics.
CPU > 70% β Add 1 task
CPU < 30% β Remove 1 task
Minimum = 2 tasks
Maximum = 10 tasks
ECS works well for most production needs, but for multi-container, complex workloads, Kubernetes is a better option.
brew install eksctl
ksctl create cluster --name django-cluster --region us-east-1 --nodes 3
kubectl apply -f deployment.yaml
kubectl expose deployment django-deploy --type=LoadBalancer --port=80 --target-port=8000
You've now deployed a production-grade Django + DRF application using Docker, AWS ECS, and Fargate. Your app is running on a highly available, auto-scaling infrastructure with secure HTTPS, automated scaling, and centralized logging.
βοΈ Dockerized our Django + DRF app
βοΈ Pushed the Docker image to AWS ECR
βοΈ Deployed using ECS with Fargate
βοΈ Configured load balancing with ALB
βοΈ Secured with AWS ACM and HTTPS
βοΈ Automated scaling and monitoring with CloudWatch
Reach out for discussing your infrastructure and deployment strategies: AhmadWKhan.com
Happy Deployment! :)