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.