Deploy Django Across Multiple Regions
Plan a multi-region Django deployment: decide between active-passive and active-active, route users with latency or geo DNS, place PostgreSQL primaries and read replicas, handle sessions, caches, Celery, and media per region, and test regional failover.
Problem statement
Your Django app serves users on several continents, or your availability target cannot tolerate the loss of a whole cloud region. Running web servers in several regions is easy; the hard part is data. A single PostgreSQL primary means writes from far regions cross oceans, and regional failover needs promoting a replica, switching DNS, and accepting possible data loss. The architecture you choose depends on whether the goal is lower latency, higher availability, or both.
- Decide the goal: disaster recovery (active-passive) or global latency (active-active reads).
- Active-passive: full stack in a secondary region with a cross-region read replica, promoted during a regional outage.
- Active-active reads: web servers and read replicas in each region, all writes routed to one primary region.
- Keep sessions, caches, and Celery queues regional; use
cached_dbor signed cookie sessions so a region switch does not log users out. - Route with latency-based or geo DNS with health checks, and rehearse failover regularly.
Step-by-step solution
1. Choose the pattern
- Active-passive (DR): one region serves all traffic; the other runs a warm copy with replicated data. Simple, with some downtime during failover and possible loss of seconds of writes (RPO).
- Active-active reads: every region serves traffic; reads hit local replicas, writes go to the primary region. Lower latency for read-heavy apps; writes from far regions stay slow.
- Multi-primary: writes in every region, using a distributed database (CockroachDB, Aurora DSQL, Spanner-like systems) or careful sharding by user region. Most complex; needed only for global write latency or strict regional data residency.
2. Route reads and writes
With active-active reads, each region's Django uses two database aliases:
DATABASES = {
"default": {**db_config(os.environ["PRIMARY_DATABASE_URL"])}, # primary region
"replica": {**db_config(os.environ["LOCAL_REPLICA_URL"])}, # same region
}
DATABASE_ROUTERS = ["config.routers.PrimaryReplicaRouter"]
Route reads to replica except right after a write by the same user (read-your-writes): set a short-lived cookie or session flag after writes and send that user's reads to the primary for a few seconds. Cross-region replica lag is typically under a second but can spike.
3. Keep stateful services regional
- Cache: one Redis per region; cache misses after a region switch are acceptable.
- Sessions:
signed_cookiesorcached_dbsessions survive a region switch; cache-only sessions do not. - Celery: a broker per region, with tasks writing to the primary database. Beat runs in exactly one region at a time; move it during failover.
- Media: object storage with cross-region replication (S3 CRR, GCS dual-region) or a CDN in front of one bucket.
4. Route traffic with health checks
Latency-based or geolocation DNS (Route 53, Cloudflare Load Balancing, Google Cloud DNS) sends users to the nearest healthy region. Health checks should test a Django endpoint that verifies the database is writable in the primary region and readable in replicas. Keep DNS TTLs short (60 seconds) for faster failover.
5. Plan and rehearse failover
For a primary region outage:
- Promote the replica in the secondary region to primary.
- Point
PRIMARY_DATABASE_URLin all regions at the new primary (or use a DNS name you update). - Move Celery beat and any singleton jobs.
- Shift traffic away from the failed region in DNS.
Script it, run it in a game day on a schedule, and measure recovery time (RTO) and data loss (RPO).
Explanation
Django's stateless web tier scales horizontally anywhere, so multi-region is mostly a database and state problem. PostgreSQL replication across regions is asynchronous to avoid adding cross-ocean latency to every commit, which is why failover can lose recent writes and why reads from replicas can be slightly stale. Database routers let Django send reads locally and writes to the primary, and careful handling of sessions, caches, and singleton jobs keeps the app correct when traffic moves between regions.
Edge cases / notes
- Data residency. Laws may require some users' data to stay in a region; that pushes toward sharding by region rather than replicating everything.
- Migrations. Schema changes apply on the primary and replicate; long migrations increase replica lag in every region.
- Cost. Running full stacks in several regions roughly multiplies infrastructure cost; DR standby can be scaled down until needed.
Next steps
- Handle failover in one region first with PostgreSQL High Availability and Failover for Django.
- Route reads with Use Read Replicas With Django.
- Write it down in a Django Disaster Recovery Plan.
FAQ
Do I need multi-region for high availability?
Not usually. Multi-AZ deployments within one region cover most failures. Multi-region protects against full regional outages.
Can Django write to databases in several regions?
Only with a multi-primary database or sharding. Standard PostgreSQL has one primary.
How do users stay logged in after a region switch?
Use signed cookie or cached_db sessions, not cache-only sessions stored in a regional Redis.
What happens to Celery beat in multiple regions?
Run it in exactly one region and move it during failover, or periodic tasks run twice.