Deploy Django on Amazon EKS
Deploy Django on Amazon EKS: build images in ECR, expose the app through an ALB with the AWS Load Balancer Controller and ACM certificates, connect to RDS, grant AWS access with Pod Identity, sync secrets, and run migrations as Jobs.
Problem statement
Your organisation standardises on Kubernetes and AWS, and the Django app needs to run on Amazon EKS alongside other services. EKS gives you a managed control plane; you still need to connect the AWS pieces correctly: images in ECR, an Application Load Balancer created from Kubernetes Ingress resources, certificates from ACM, RDS PostgreSQL in private subnets, IAM permissions for S3 and SES without access keys, and secrets that do not live in Git.
- Create the cluster with
eksctl(or Terraform), using managed node groups or Fargate profiles, in private subnets. - Install the AWS Load Balancer Controller; create an Ingress with
ingressClassName: alband an ACM certificate ARN. - Run RDS PostgreSQL in private subnets; allow the node or pod security group on port 5432.
- Use EKS Pod Identity (or IRSA) so pods get IAM roles for S3, SES, and Secrets Manager.
- Sync secrets from Secrets Manager with External Secrets Operator or the Secrets Store CSI driver.
- Run
migrateas a Job before each rollout; set readiness probes and graceful shutdown.
Step-by-step solution
1. Cluster and image registry
eksctl create cluster --name django-prod --region eu-west-1 --version 1.31 \
--nodegroup-name web --node-type t3.large --nodes 2 --nodes-min 2 --nodes-max 5 --node-private-networking
aws ecr create-repository --repository-name myapp
Build and push images tagged with the commit SHA. Enable the EKS Pod Identity Agent add-on for IAM access.
2. Load balancer and HTTPS
Install the AWS Load Balancer Controller (Helm chart eks/aws-load-balancer-controller) with its IAM role. Then:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: app
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80},{"HTTPS":443}]'
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:eu-west-1:123456789012:certificate/abc
alb.ingress.kubernetes.io/healthcheck-path: /health/
spec:
ingressClassName: alb
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: web, port: { number: 80 } } }
target-type: ip sends traffic straight to pod IPs. The ALB terminates TLS, so Django needs SECURE_PROXY_SSL_HEADER, and the health check path must pass ALLOWED_HOSTS (send no Host requirement on /health/, or allow pod IPs for that path through middleware).
3. RDS connectivity
Create RDS PostgreSQL (Multi-AZ for production) in the cluster VPC's private subnets. Allow inbound 5432 from the node security group, or use Security Groups for Pods to allow only Django pods. Require TLS with sslmode=require in DATABASE_URL. For many replicas, consider RDS Proxy to cap connections.
4. IAM for pods
aws eks create-pod-identity-association --cluster-name django-prod --namespace app \
--service-account django --role-arn arn:aws:iam::123456789012:role/django-app
The role's policy grants the bucket, SES identity, and secrets the app needs. boto3 in the pod picks up credentials automatically; remove any AWS_ACCESS_KEY_ID settings.
5. Secrets
External Secrets Operator syncs Secrets Manager values into Kubernetes Secrets:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata: { name: django-env, namespace: app }
spec:
secretStoreRef: { name: aws-secrets, kind: ClusterSecretStore }
target: { name: django-env }
dataFrom: [{ extract: { key: prod/django } }]
Mount it with envFrom in the Deployment.
6. Migrations and rollout
Run a Job using the same image and service account, wait for completion, then update the Deployment image. Set terminationGracePeriodSeconds above Gunicorn's graceful timeout and the ALB deregistration delay so in-flight requests finish.
Verification check: kubectl get ingress -n app shows an ALB address, the ALB target group lists healthy pod IPs, https://example.com serves the ACM certificate, and aws sts get-caller-identity run inside a pod returns the app role.
Explanation
EKS provides upstream Kubernetes with an AWS-managed control plane, and AWS integration happens through controllers and add-ons running in the cluster. The AWS Load Balancer Controller watches Ingress objects and creates ALBs and target groups; Pod Identity injects role credentials into pods; External Secrets reads Secrets Manager. Django itself is a standard containerised WSGI app; the deployment work is wiring these integrations so that the app reaches its database and AWS services securely and receives traffic only when ready.
Edge cases / notes
- Fargate profiles remove node management but do not support DaemonSets and some add-ons; check compatibility.
- Celery runs as separate Deployments; Beat as a single replica or a leader-elected scheduler.
- Cost. EKS charges per cluster hour plus nodes and ALBs; for a single app, ECS Fargate is usually cheaper and simpler.
- Upgrades. EKS versions have a support window; plan cluster upgrades yearly.
Next steps
- General Kubernetes patterns: Deploy Django on Kubernetes.
- Simpler AWS container option: Deploy Django on AWS ECS Fargate.
FAQ
Should I use EKS or ECS for Django?
ECS is simpler for a single app. EKS fits when your organisation already runs Kubernetes or needs its ecosystem.
How do Django pods get AWS permissions?
Through EKS Pod Identity or IRSA, which map a Kubernetes service account to an IAM role.
How is HTTPS handled on EKS?
The AWS Load Balancer Controller creates an ALB with an ACM certificate from an Ingress resource.
Where should migrations run?
In a Kubernetes Job before updating the Deployment.