1507 words
8 minutes
AWS for AI/Agent Developers — Day 5: Multi-Region Agent Routing with Route53

Your agent runs in one region. Users everywhere. Latency kills the experience — a 2-second LLM call from us-east-1 feels fine locally, but from Southeast Asia it’s 4 seconds. Add DNS resolution, TLS negotiation, network hops, and you’ve lost the user.

Today we build multi-region routing for agents:

  • Route53 latency-based routing — DNS resolves to the region with lowest latency for each user
  • Active-passive failover — if us-east-1 goes down, traffic shifts to eu-west-1
  • CloudFront — cache agent responses at 450+ edge locations, not just 3 AWS regions
  • Global Accelerator — TCP/UDP traffic for agents that can’t use DNS-based routing
┌─────────────────────────┐
│ Route53 │
│ Latency-based DNS │
│ Health checks │
│ Failover policies │
└────┬──────────┬─────────┘
│ │
┌─────────┘ └─────────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ us-east-1 │ │ eu-west-1 │
│ (Primary) │ │ (Secondary) │
│ │ │ │
│ ┌──────────┐ │ │ ┌──────────┐ │
│ │ ALB │ │ │ │ ALB │ │
│ │ Agent │ │ │ │ Agent │ │
│ │ Service │ │ │ │ Service │ │
│ └──────────┘ │ │ └──────────┘ │
│ │ │ │
│ DynamoDB │ ◄────► │ DynamoDB │
│ Global Table │ │ Global Table │
└────────────────┘ └────────────────┘
┌─────────▼──────────┐
│ CloudFront (CDN) │
│ 450+ edge nodes │
│ Cache agent GET │
│ responses │
└────────────────────┘

Step 1: Deploy Agent in Two Regions#

You already have the infrastructure from Days 1-4. Deploy it in us-east-1 (primary) and eu-west-1 (secondary).

Terminal window
# Deploy ECS-based agent in us-east-1 (Day 1)
aws ecs create-service \
--cluster mcp-server-cluster \
--service-name github-issue-mcp \
--task-definition github-issue-mcp \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-us-east-1a,subnet-us-east-1b],securityGroups=[sg-xxx]}" \
--load-balancers "targetGroupArn=<tg-arn-us-east-1>,containerName=mcp-server,containerPort=3001" \
--region us-east-1
# Same thing in eu-west-1
aws ecs create-service \
--cluster mcp-server-cluster \
--service-name github-issue-mcp \
--task-definition github-issue-mcp \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-eu-west-1a,subnet-eu-west-1b],securityGroups=[sg-yyy]}" \
--load-balancers "targetGroupArn=<tg-arn-eu-west-1>,containerName=mcp-server,containerPort=3001" \
--region eu-west-1

State replication#

DynamoDB Global Tables already replicate sessions across regions (Day 2). No extra work.

Cache warming#

ElastiCache Redis is regional. In failover, sessions that were in Redis hit DynamoDB instead (a bit slower, but still works).


Step 2: Route53 Latency-Based Routing#

Create agent subdomain#

Terminal window
# Create latency-based record set for us-east-1
aws route53 change-resource-record-sets \
--hosted-zone-id ZONE_ID \
--change-batch '{
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "agent.yourdomain.com.",
"Type": "A",
"SetIdentifier": "us-east-1",
"Region": "us-east-1",
"AliasTarget": {
"HostedZoneId": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region us-east-1 --query 'LoadBalancers[0].CanonicalHostedZoneId' --output text)"'",
"DNSName": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region us-east-1 --query 'LoadBalancers[0].DNSName' --output text)"'",
"EvaluateTargetHealth": true
},
"Failover": "PRIMARY",
"HealthCheckId": "'"$HEALTH_CHECK_US"'""
}
},
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "agent.yourdomain.com.",
"Type": "A",
"SetIdentifier": "eu-west-1",
"Region": "eu-west-1",
"AliasTarget": {
"HostedZoneId": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region eu-west-1 --query 'LoadBalancers[0].CanonicalHostedZoneId' --output text)"'",
"DNSName": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region eu-west-1 --query 'LoadBalancers[0].DNSName' --output text)"'",
"EvaluateTargetHealth": true
},
"Failover": "SECONDARY",
"HealthCheckId": "'"$HEALTH_CHECK_EU"'""
}
}
]
}'

Step 3: Health Checks#

Route53 health checks determine whether a region is healthy. If us-east-1 fails, Route53 automatically routes traffic to eu-west-1.

Terminal window
# Health check for us-east-1 ALB
aws route53 create-health-check \
--caller-reference "agent-us-east-1-$(date +%s)" \
--health-check-config '{
"Type": "HTTPS",
"FullyQualifiedDomainName": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region us-east-1 --query 'LoadBalancers[0].DNSName' --output text)"'",
"Port": 443,
"ResourcePath": "/health",
"RequestInterval": 30,
"FailureThreshold": 3,
"MeasureLatency": true,
"EnableSNI": true
}'
# Same for eu-west-1
aws route53 create-health-check \
--caller-reference "agent-eu-west-1-$(date +%s)" \
--health-check-config '{
"Type": "HTTPS",
"FullyQualifiedDomainName": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region eu-west-1 --query 'LoadBalancers[0].DNSName' --output text)"'",
"Port": 443,
"ResourcePath": "/health",
"RequestInterval": 30,
"FailureThreshold": 3,
"MeasureLatency": true,
"EnableSNI": true
}'

Calculate your latency budget:#

Hopus-east-1eu-west-1
Client DNS20-50ms20-50ms
Route53 latency routing10ms10ms
TLS handshake30ms30ms
ALB processing5ms5ms
LLM call (Bedrock)2-5s2-5s
Tool execution200-500ms200-500ms
DynamoDB read5-10ms5-10ms
Response transfer50ms (local) / 200ms (transatlantic)50ms

For users in Asia, us-east-1 adds 150-200ms vs eu-west-1. Route53 picks the closer region automatically.


Step 4: CloudFront for Edge Caching#

For agent responses that are cacheable (documentation lookups, static analysis results, common queries), CloudFront can serve from 450+ edge locations instead of routing to your agent infrastructure at all.

Terminal window
# Create CloudFront distribution pointing to our agent
aws cloudfront create-distribution \
--origin-domain-name "agent.yourdomain.com" \
--default-root-object "" \
--default-cache-behavior '{
"TargetOriginId": "agent-origin",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"ForwardedValues": {
"QueryString": true,
"Cookies": { "Forward": "none" },
"Headers": {
"Quantity": 1,
"Items": ["Authorization"]
}
},
"MinTTL": 60,
"DefaultTTL": 300,
"MaxTTL": 3600
}' \
--enabled \
--comment "Agent CDN - Cache agent responses at edge"

What to cache at edge vs what to pass through:

CacheableNot cacheable
Static docs, FAQ responsesReal-time agent reasoning
Knowledge base lookupsUser-specific prompts
Tool schema definitionsSession state changes
Health check responsesWebSocket streams
Config metadataWrite operations (create, delete)

Step 5: Global Accelerator for TCP/UDP Agents#

Route53 handles DNS routing well, but DNS changes take time to propagate (TTL-dependent). Global Accelerator uses Anycast IPs — traffic enters the AWS network at the closest edge and is routed internally to the optimal region.

This is important for agents that need:

  • Fast failover (<1s, vs DNS’s 30-60s TTL propagation)
  • TCP/UDP protocols (MCP STDIO alternative transports)
  • Non-HTTP agents (gRPC, raw TCP sockets)
Terminal window
# Create Global Accelerator
ACCELERATOR_ARN=$(aws globalaccelerator create-accelerator \
--name agent-global-accelerator \
--ip-address-type IPV4 \
--enabled \
--query 'Accelerator.AcceleratorArn' --output text)
# Get the two Anycast IPs
aws globalaccelerator describe-accelerator \
--accelerator-arn $ACCELERATOR_ARN \
--query 'Accelerator.IpSets[0].IpAddresses'
# Create listener (port 443, TCP)
LISTENER_ARN=$(aws globalaccelerator create-listener \
--accelerator-arn $ACCELERATOR_ARN \
--port-ranges '[{"FromPort": 443, "ToPort": 443}]' \
--protocol TCP \
--client-affinity NONE \
--query 'Listener.ListenerArn' --output text)
# Create endpoint group for us-east-1
aws globalaccelerator create-endpoint-group \
--listener-arn $LISTENER_ARN \
--endpoint-group-region us-east-1 \
--traffic-dial-percentage 100 \
--health-check-port 443 \
--health-check-protocol HTTPS \
--health-check-path /health \
--endpoint-configurations '[{
"EndpointId": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region us-east-1 --query 'LoadBalancers[0].LoadBalancerArn' --output text)"'",
"Weight": 100
}]'
# Same for eu-west-1
aws globalaccelerator create-endpoint-group \
--listener-arn $LISTENER_ARN \
--endpoint-group-region eu-west-1 \
--traffic-dial-percentage 100 \
--health-check-port 443 \
--health-check-protocol HTTPS \
--health-check-path /health \
--endpoint-configurations '[{
"EndpointId": "'"$(aws elbv2 describe-load-balancers --names mcp-server-alb --region eu-west-1 --query 'LoadBalancers[0].LoadBalancerArn' --output text)"'",
"Weight": 50
}]'

Route53 vs Global Accelerator:

FeatureRoute53Global Accelerator
ProtocolHTTP/HTTPS onlyTCP/UDP + HTTP/HTTPS
Failover time30-60s (DNS TTL)<1s (Anycast)
Latency optimizationDNS-levelNetwork-level
Session persistenceLimitedClient affinity
CostIncluded with hosted zone~$0.025/hr per accelerator

Step 6: Full Agent Failover Test#

#!/bin/bash
# failover-test.sh — Test multi-region agent failover
REGION_CHECK_ENDPOINT="agent.yourdomain.com/health"
PRIMARY_REGION="us-east-1"
SECONDARY_REGION="eu-west-1"
echo "=== 1. Normal state: both regions healthy ==="
curl -s -o /dev/null -w "Status: %{http_code}, Latency: %{time_total}s, IP: %{remote_ip}\n" \
https://agent.yourdomain.com/health
echo ""
echo "=== 2. Get current routing from multiple locations ==="
for endpoint in "us-east-1" "eu-west-1" "ap-southeast-1"
do
echo "Probing from $endpoint:"
# Using Route53 resolver to simulate different client locations
dig agent.yourdomain.com @8.8.8.8 +short
dig agent.yourdomain.com @1.1.1.1 +short
dig agent.yourdomain.com @208.67.222.222 +short
done
echo ""
echo "=== 3. Simulate us-east-1 failure ==="
# Block health check access to simulate ALB failure
aws ec2 revoke-security-group-ingress \
--group-id <alb-sg-us-east-1> \
--protocol tcp --port 443 \
--source-group <health-check-sg>
echo "Waiting for Route53 health check to fail (90s)..."
sleep 90
# Kill ALB instances
aws autoscaling set-desired-capacity \
--auto-scaling-group-name <asg-us-east-1> \
--desired-capacity 0
echo ""
echo "=== 4. Verify failover to eu-west-1 ==="
curl -s -o /dev/null -w "Status: %{http_code}, Latency: %{time_total}s\n" \
https://agent.yourdomain.com/health
echo ""
echo "=== 5. Restore us-east-1 ==="
aws autoscaling set-desired-capacity \
--auto-scaling-group-name <asg-us-east-1> \
--desired-capacity 2
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name <asg-us-east-1> \
--health-check-type ELB --health-check-grace-period 60
echo "Waiting for health check recovery..."
sleep 60
aws ec2 authorize-security-group-ingress \
--group-id <alb-sg-us-east-1> \
--protocol tcp --port 443 \
--source-group <health-check-sg>
echo ""
echo "=== 6. Verify recovery ==="
curl -s -o /dev/null -w "Status: %{http_code}, Latency: %{time_total}s\n" \
https://agent.yourdomain.com/health
echo ""
echo "=== Failover test complete ==="

Step 7: Client-Side Multi-Region Awareness#

For agents that need session awareness across regions (e.g., same conversation, different region), the client sends its region preference:

const agentClient = {
region: await detectClosestRegion(),
async detectClosestRegion(): Promise<string> {
// Probe each region endpoint
const regions = ["us-east-1", "eu-west-1", "ap-southeast-1"];
const latencies = await Promise.all(
regions.map(async (region) => {
const start = Date.now();
try {
await fetch(`https://agent-${region}.yourdomain.com/ping`);
return { region, latency: Date.now() - start };
} catch {
return { region, latency: Infinity };
}
})
);
return latencies.sort((a, b) => a.latency - b.latency)[0].region;
},
async tell(prompt: string) {
const region = this.region;
const url = `https://agent-${region}.yourdomain.com/agent`;
return fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
prompt,
sessionId: this.sessionId,
currentRegion: region,
}),
});
},
};

Cost Breakdown#

ComponentMonthly
Second region (ECS Fargate, ALB)~$55/mo
DynamoDB Global Tables replication~$20-40/mo
Route53 latency records (2 regions)~$1/mo
Route53 health checks (2 checks × 3 locations)~$6/mo
CloudFront (10GB, 100K requests)~$5/mo
Global Accelerator (optional)~$18/mo
Total additional for DR~$87-125/mo

For a single region agent costing ~69/mo(fromDay1),addingmultiregionDRcostsroughly1.52×thebaseinfrastructure.Worthitifagentdowntimecostsmorethan 69/mo (from Day 1), adding multi-region DR costs roughly 1.5-2× the base infrastructure. Worth it if agent downtime costs more than ~4/day.


Summary#

LayerTechnologyPurpose
DNS routingRoute53 latency-basedRoute to nearest region
FailoverRoute53 active-passiveAuto-detect and route around failures
Edge cachingCloudFrontServe cacheable responses from 450+ locations
Anycast routingGlobal AcceleratorTCP/UDP agents, sub-second failover
State replicationDynamoDB Global TablesSession survival across regions
Health checksRoute53 health checksMonitor region health

Checklist:#

  • Agent infrastructure deployed in 2+ regions
  • DynamoDB Global Tables enabled (Day 2)
  • Route53 latency-based records created
  • Health checks configured (HTTPS /health endpoint)
  • Active-passive failover with PRIMARY/SECONDARY
  • CloudFront distribution for cacheble responses
  • Global Accelerator for TCP/UDP agents (optional)
  • Failover test script run and verified
  • Client-side region detection configured
  • Cost budgeted for second region

DayTopic
1Deploy MCP Server on ECS Fargate ✅
2Agent State with DynamoDB Global Tables ✅
3LLM Caching with ElastiCache + Bedrock ✅
4Serverless Agent with Lambda + Bedrock ✅
5Multi-Region Agent Routing with Route53 ✅
6CI/CD for AI Agents with CodePipeline

Series: AWS for AI/Agent Developers. Day 5: Multi-region agent routing — Route53 latency-based routing, active-passive failover, CloudFront edge caching, Global Accelerator for TCP/UDP.

Advertisement

AWS for AI/Agent Developers — Day 5: Multi-Region Agent Routing with Route53
https://minixium.com/en/posts/aws-for-ai-agent-developers-multi-region-agent-routing/
Author
Minixium
Published at
2026-06-30
License
CC BY-NC-SA 4.0

Advertisement