A production Django app on AWS usually runs gunicorn or uvicorn in containers on ECS with Fargate behind a load balancer, with PostgreSQL on RDS or Aurora, ElastiCache (or SQS) for Celery, and S3 plus CloudFront for static and media. Your models and views do not change. The work is the Dockerfile, environment-driven settings, the Celery tier, deploy steps, and the database cutover.
Django apps arrive at AWS from a PaaS (Heroku, Render, DigitalOcean App Platform) or from a single VPS. The reasons rhyme.
Cost as you grow. A PaaS charges a premium for running the platform. Once you run several web processes, Celery workers, managed Postgres and Redis around the clock, buying the underlying services on AWS (Fargate, RDS, ElastiCache) and right-sizing them can cost less. It is not automatic: price both sides with your real usage before you decide.
The architecture Django apps need. A real deployment is a web tier, a Celery worker tier, a beat scheduler, Postgres, a broker and cache, and somewhere for static and media. ECS models each tier as its own service with its own scaling, inside your VPC.
Surrounding requirements. Private subnets for the database, IAM, multi-AZ failover, compliance controls and integration with S3, SQS, SES and KMS are first-class on AWS.
The counterpoint: AWS asks you to own more (networking, IAM, images, pipelines, monitoring). That is why many teams have a partner run the move.
Database, broker, cache, static and media map almost one-to-one. The compute choice is the main decision.
Compute. Amazon ECS on Fargate is the usual home: serverless containers with VPC control, per-service scaling, a load balancer, and clean separation of web, workers and beat. For a simple web service, ECS Express Mode creates the Fargate service, load balancer and scaling from one image. (App Runner closed to new customers on April 30, 2026.) EKS is overkill for one Django app unless you already run Kubernetes.
Web server. gunicorn for synchronous apps (start around 2 × vCPU + 1 workers and tune with load tests), or gunicorn with uvicorn.workers.UvicornWorker for async views and Channels. The load balancer terminates TLS with an ACM certificate.
Database. RDS for PostgreSQL (like-for-like) or Aurora PostgreSQL (storage that grows on its own, faster failover, up to 15 readers). The ORM speaks PostgreSQL either way, so you only change DATABASES. Keep it in private subnets.
Celery. ElastiCache for Redis OSS or Valkey as broker and result backend, doubling as Django's cache. Or Amazon SQS as the broker to drop a stateful component (SQS is not a result backend). Each worker pool runs as its own ECS service; beat runs as a single-replica service so schedules do not double-fire.
Static and media. collectstatic to S3 behind CloudFront via django-storages, or WhiteNoise for small apps. User uploads must go to S3: container disks are ephemeral and there are several tasks.
Turning the app into a production image and moving configuration into AWS-managed config is the most hands-on task.
The Dockerfile is short: a slim Python base, install from your lockfile, copy the project, run as a non-root user, start gunicorn or uvicorn. The same image serves every tier: the web service runs the server, the worker service overrides the command to celery -A proj worker, and beat overrides it to celery -A proj beat.
Drive settings from environment variables (DEBUG, ALLOWED_HOSTS, DATABASE_URL, CELERY_BROKER_URL, SECRET_KEY). Put real secrets in AWS Secrets Manager and plain config in SSM Parameter Store; ECS injects both into the task, so your code still reads os.environ and nothing sensitive is baked into an image.
Two production details: set ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS and SECURE_PROXY_SSL_HEADER for running behind a load balancer, and plan connection pooling (RDS Proxy or PgBouncer) because persistent connections across many autoscaled tasks can exhaust Postgres connections.
With several tasks starting in parallel behind a load balancer, running these at boot causes races and flaky deploys.
Run collectstatic at build time (into the image, or into S3). Run migrate as a single one-off ECS task per deploy, wait for it to succeed, then roll the web and worker services. Keep migrations backward-compatible (add before you remove) so old and new tasks can coexist during a rolling deploy.
1. Build the image and run collectstatic. 2. Push to ECR. 3. Run a one-off migrate task and wait. 4. Update the web service. 5. Update the worker and beat services.
Everything else can be built and tested without touching production. The database cutover is the one moment that touches live data.
AWS Database Migration Service (DMS) does a full load into RDS or Aurora, then streams ongoing changes (CDC) where your source database allows it, so at cutover you switch to an already-current copy. Same engine, so no schema conversion. If CDC is not possible from your current host, or the database is small, use pg_dump and pg_restore inside a maintenance window and time a test run first.
The cutover: maintenance page, drain the last changes, pause beat and let workers finish, switch DATABASES and the broker and cache URLs, update Route 53 (TTL lowered a day ahead), check that writes land in RDS, leave maintenance. After a data-only load, re-sync Postgres sequences or the first inserts collide on primary keys. Keep the old environment as a rollback until you are confident.
You can do this yourself with the steps above. Many teams have a partner run the landing zone, the container and Celery setup, and the cutover.
Partner-filed funding: AWS says the Migration Acceleration Program (MAP) helps offset initial migration costs. It is built for migrations with meaningful ongoing AWS spend, so a single small Django app may not qualify. When the migration does qualify, your matched partner files it and AWS approves: typically $50K to $150K for startups. Depends on the workload; AWS approves. Activate credits are separate, and you apply for those yourself.
CloudRoute matches you with one vetted AWS partner. Startups pay CloudRoute nothing; partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
| Funding | Who files it | Amount | When it fits |
|---|---|---|---|
| Migration Acceleration Program (MAP) | Your matched AWS partner | $50K to $150K for startups. Depends on the workload; AWS approves. | You are not on AWS yet and want to migrate. |
| AWS Activate Founders | Founder applies | $1,000 to start, up to $5,000 | Self-funded startups with no investor or accelerator behind them. |
| AWS Activate Portfolio | Founder applies | up to $200,000 | Startups backed by a VC firm, accelerator or angel investor that is an AWS Activate Provider. |
Every Django building block, where it lands on AWS, what changes, and the effort. "Low" rows are config or backend swaps; "Medium" and "High" rows are the real work.
| Django component | AWS service | What changes | Effort |
|---|---|---|---|
| WSGI web (gunicorn) | ECS on Fargate plus ALB (ECS Express Mode for simple apps) | Containerise; ALB terminates TLS | Medium |
| ASGI, async, Channels (uvicorn) | ECS on Fargate plus ALB | uvicorn worker class; WebSocket settings | Medium |
| PostgreSQL | RDS for PostgreSQL or Aurora PostgreSQL | DATABASES endpoint; cutover via DMS or dump and restore | High (cutover) |
| Celery broker and results | ElastiCache (Redis OSS or Valkey) or SQS | CELERY_BROKER_URL; or SQS transport | Medium |
| Celery workers | ECS service per queue | Worker process becomes its own service | Medium |
| Celery beat | Single-replica ECS service, or EventBridge Scheduler | Run exactly one beat | Low |
| Cache | ElastiCache | CACHES backend to the ElastiCache endpoint | Low |
| Static files | S3 plus CloudFront (django-storages) or WhiteNoise | Storage backend; collectstatic in CI | Low |
| Media uploads | Amazon S3 (django-storages) | Default storage to S3 | Low |
| Settings and secrets | SSM Parameter Store and Secrets Manager | Injected as env vars by ECS | Low |
| Amazon SES (or your provider) | EMAIL_BACKEND settings | Low | |
| Domain and TLS | Route 53, ACM, CloudFront | DNS and a free certificate | Low |
You fill in a short form: what you run today, what you want to move, test or build, your stage and your region.
We read your answers and pick one vetted AWS consulting partner that fits the work, the stack and the region. We reply within 24 hours; your matched partner reaches out within 5 business days.
Your matched partner scopes the migration with you. Then it checks whether the move qualifies for MAP (Migration Acceleration Program) funding. MAP is for teams that are not on AWS yet.
If the work qualifies, the partner files the funding request with AWS. AWS reviews it and decides. The amount depends on the workload.
CloudRoute is free for startups. Partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
AWS Activate credits are separate, and you apply for them yourself. Use Activate Founders if no investor backs you. Use Activate Portfolio with the Org ID or invite from your VC, accelerator, incubator or angel group if it is an Activate Provider. A consulting partner cannot apply for Activate on your behalf.
CloudRoute matches you with one vetted AWS partner who plans and runs the Django migration: ECS on Fargate, RDS or Aurora, ElastiCache for Celery, S3 and CloudFront, and the database cutover. The partner files MAP funding when the migration qualifies and AWS approves. CloudRoute is free for startups.