Skip to main content

Multi-Region Deployments

Why Multi-Region?​

1. Low Latency​

User requests served from nearby region:

Multi-region latency

vs. everyone going to us-east-1 (200+ ms for international).


Simple Explanation​

What it is​

Multi-region means running your app in more than one geographic region so users are served from the closest location.

Why we need it​

Global users expect fast responses and reliable service even if a region goes down.

Benefits​

  • Lower latency for users around the world.
  • Higher availability when a region fails.
  • Compliance support for data residency rules.

Tradeoffs​

  • Higher complexity for data replication.
  • More cost for running multiple regions.

Real-world examples (architecture only)​

  • Global API -> Route users to nearest region.
  • Primary region down -> Failover to secondary region.

2. High Availability​

If one region fails, others serve traffic.

3. Compliance​

Data residency requirements (GDPR, etc.).

Architecture​

Primary Region​

Full stack in primary region:

  • API Gateway
  • Lambda functions
  • DynamoDB
  • S3 buckets

Secondary Regions​

Read-only replicas + lightweight API:

Multi-region architecture

Route 53: Global Routing​

Amazon Route 53 directs users to nearest region:

HealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
Type: HTTPS
ResourcePath: /health
FullyQualifiedDomainName: api-us.example.com

RecordSet:
Type: AWS::Route53::RecordSet
Properties:
Name: api.example.com
Type: A
SetIdentifier: US
GeoLocation:
CountryCode: US
AliasTarget:
DNSName: api-us.example.com
HostedZoneId: Z1234567
HealthCheckId: !Ref HealthCheck

Route 53 automatically:

  • Sends US users to us-east-1
  • Sends EU users to eu-west-1
  • Sends Asia users to ap-northeast-1
  • Fails over if endpoint is down

DynamoDB Global Tables​

Replicate DynamoDB across regions:

ItemsTable:
Type: AWS::DynamoDB::GlobalTable
Properties:
SSESpecification:
SSEEnabled: true
BillingMode: PAY_PER_REQUEST
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
Replicas:
- Region: us-east-1
- Region: eu-west-1
- Region: ap-northeast-1

Bi-directional replication:

  • Write in US region
  • Automatically replicated to EU and Asia (< 1 second)
  • Read from any region instantly

S3 Cross-Region Replication​

Replicate S3 buckets across regions:

# Create bucket in us-east-1
aws s3api create-bucket \
--bucket my-app-us

# Create bucket in eu-west-1
aws s3api create-bucket \
--bucket my-app-eu \
--region eu-west-1

# Enable replication
aws s3api put-bucket-replication \
--bucket my-app-us \
--replication-configuration {
"Role": "arn:aws:iam::123:role/s3-replication",
"Rules": [{
"Status": "Enabled",
"Priority": 1,
"Destination": {
"Bucket": "arn:aws:s3:::my-app-eu"
}
}]
}

New files in us-east-1 automatically replicate to eu-west-1.

Lambda@Edge​

Run Lambda functions at CloudFront edge locations (68 locations worldwide):

CloudFrontFunction:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.12
Handler: index.handler
Code:
ZipFile: |
exports.handler = async (event) => {
// Runs at all 68 CloudFront locations worldwide
const request = event.Records[0].cf.request;
return request;
};

Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
DefaultCacheBehavior:
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref CloudFrontFunction

Latency: < 50ms from anywhere.

Database Consistency​

Strong consistency (same region):

# Read immediately after write
ddb.put_item(Item={"id": "123", "name": "Alice"})
result = ddb.get_item(Key={"id": "123"})
# result["Item"]["name"] == "Alice" (guaranteed)

Eventual consistency (global tables):

# Write in us-east-1
# Replicate to eu-west-1 (< 1 second delay)

# If reading immediately after write in different region:
result = ddb_eu.get_item(Key={"id": "123"})
# result might be old (or not exist yet)

Solution: Use strongly-consistent reads if needed (higher latency).

Deployment​

CI/CD Multi-Region​

Deploy to all regions on push:

# GitHub Actions workflow
deploy:
strategy:
matrix:
region: [us-east-1, eu-west-1, ap-northeast-1]
steps:
- run: sam deploy --region ${{ matrix.region }}

Failover​

If primary region goes down:

PrimaryHealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
Type: HTTPS
Endpoint: api-us.example.com

FailoverRecord:
Type: AWS::Route53::RecordSet
Properties:
Name: api.example.com
Failover: PRIMARY
SetIdentifier: US-Primary
AliasTarget:
DNSName: api-us.example.com
HostedZoneId: Z123

FailoverRecordSecondary:
Type: AWS::Route53::RecordSet
Properties:
Name: api.example.com
Failover: SECONDARY
SetIdentifier: EU-Secondary
AliasTarget:
DNSName: api-eu.example.com
HostedZoneId: Z456

If us-east-1 fails health check, traffic automatically routes to eu-west-1.

Costs​

Multi-region increases costs:

  • DynamoDB Global Tables: 2x replica cost
  • S3 Replication: Outgoing data transfer costs
  • Lambda: Replicate code in each region
  • Route 53: Minimal ($0.50/hosted zone)

Trade-off: 3-5x cost for 5x better availability.

Worth it for critical apps, not for hobby projects.

Disaster Recovery (DR)​

RPO (Recovery Point Objective): How much data can you lose?

  • Strong replication: < 1 second
  • Manual backups: < 1 day

RTO (Recovery Time Objective): How quickly can you recover?

  • Global Tables: < 1 second
  • Manual restore: > 1 hour

Best Practices​

  1. Start with one region — Add regions as traffic grows
  2. Use Route 53 for routing — Geo-aware failover
  3. Enable Global Tables — Automatic replication
  4. Monitor cross-region latency — Alert if > 2 second replication lag
  5. Test failover regularly — Know your recovery time

Hands-On: Deploy to Multiple Regions​

  1. Create SAM template with cross-region resources
  2. Deploy to us-east-1
  3. Deploy same template to eu-west-1
  4. Setup Route 53 geo-routing
  5. Test latency from different locations

Key Takeaway​

Multi-region deployments require planning but deliver massive reliability and performance improvements. Start simple (one region), evolve to global.