aws_region, so you run one set of instances per region and tie them together with a Route 53 module using latency routing. Route 53 answers each query with the region closest to the resolver and skips regions whose load balancer is unhealthy.
This guide builds on Custom domains.
Choose a topology
The Route 53 records are the same in every topology — only the record name changes. Latency routing needs a hostname Route 53 owns, so if your public domain is managed elsewhere, delegate a subdomain such as
origin.example.com to a Route 53 hosted zone and use the CloudFront topology.
Step 1: Duplicate the regional stack
Add one copy of your network, certificate, cluster, and web service per region. Use region-suffixedgivenIds and set aws_region on the modules that take it. Give each web service build_source: ecr so Ravion manages an ECR repository in that region without a per-region build; the pipeline in the next section pushes the image to all of them.
ravion.yaml
- Certificates are regional. An ALB’s ACM certificate must be issued in the ALB’s region, so each region needs its own
rvn-acm-certificate. A wildcard keeps one certificate valid for the public hostname and any origin hostname you add later. - Every region gets the same
host_header_values. Whichever region a request lands in, the listener rule must match the hostname users typed — or, with CloudFront, the hostname CloudFront forwards.
Step 2: Build once, deploy everywhere
Use a single pipeline: onebuild:image step pushes the same image to every region’s ECR repository, then a deploy step per region releases that one digest. Each service’s repository ARN is the ecr_repository_arn output of its stack.
pipeline.yaml
parallel if one region should act as a canary. There is no atomic multi-region switch: while the deploy steps run, regions briefly serve different versions.
Step 3: Create latency records
Route 53 alias records need each region’s load balancer DNS name and hosted zone ID. Both are outputs of the module that owns the ALB; read them from the module’s Outputs tab orravion stack get <stack-id> --json.
Today you copy these values into the Route 53 module by hand — the
records input doesn’t yet
accept a moduleGivenIdRef. If you recreate a load balancer, update the alias values too.rvn-route53 module with one A alias record per region, all sharing the same name. Three fields turn an alias into a latency record: routing_policy: latency, a unique set_identifier, and latency_routing_policy_region set to the region the load balancer is in.
ravion.yaml
alias_evaluate_target_health: true on every record. Route 53 then reads the ALB’s own target health — no extra health check needed — and stops returning a region whose targets are unhealthy, so users fail over to the next-closest region within a minute or two. Repeat the records with type: AAAA if you serve IPv6.
If a regional target isn’t an AWS load balancer (a Cloudflare tunnel, another provider’s
endpoint), use a
CNAME record instead: target_type: standard, the hostname in
record_value, a low ttl, and a Route 53 health check in health_check_id — CNAMEs can’t use
alias_evaluate_target_health.Step 4 (optional): Put CloudFront in front
Keep the latency records from Step 3 but rename them to an origin hostname, then add a CloudFront distribution and a public record that points at it.1
Rename the latency records
Change
name on each latency record from api.example.com to origin.example.com. Everything
else stays the same.2
Add a CloudFront module with a custom origin
Point CloudFront forwards the viewer’s
origin_domain_name at the latency-routed hostname. CloudFront needs its own certificate
in us-east-1:ravion.yaml
Host header (api.example.com) to the origin, so keep it in
each region’s host_header_values. The wildcard certificate from Step 1 covers both names.3
Point the public hostname at CloudFront
In Route 53, add a plain alias for If the public zone is with another DNS provider, create a
api.example.com:ravion.yaml
CNAME to the distribution domain
there instead. Only origin.example.com needs to live in Route 53.origin.example.com itself, so Route 53 answers with the region closest to that edge: a viewer in Frankfurt reaches eu-west-1, a viewer in Chicago reaches us-east-2. Regional failover still comes from alias_evaluate_target_health on the origin records.
Step 5: Verify routing
1
Test each regional endpoint directly
host_header_values doesn’t match.2
Ask Route 53 what it answers from another location
Route 53 honors EDNS client subnet, so you can query an authoritative name server as if you
were a resolver elsewhere:In the CloudFront topology, query
origin.example.com instead.3
Rehearse a failover
Scale one region’s service to zero tasks, watch
dig +subnet for that region flip to the
other one within a couple of minutes, then roll back.What multi-region doesn’t solve
Latency routing spreads stateless compute. Everything else needs its own plan:- Databases and caches are regional. Either accept cross-region latency to a single primary, or use a replicated store (Aurora Global Database, DynamoDB global tables) and point each region’s service at its local endpoint.
- Parameter Store is regional; Secrets Manager isn’t. Replicate a Secrets Manager secret to every region and use the regional replica ARN in each service’s
valueFrom. Parameter Store has no replication, so create parameters per region. See Managing secrets. - Sessions and uploads. Anything on the instance or in a single-region bucket won’t follow a user routed elsewhere. Use tokens for sessions and S3 Cross-Region Replication, or one bucket behind CloudFront, for objects.