Subchapter 91.3
references/configuring-failover-routing.mdMarkdown15 KBView on GitHub
Domain expertise for active-passive failover of an application running in two AWS Regions for disaster recovery (DR): sending traffic to a primary endpoint by default, monitoring its health, and shifting to a secondary when the primary is unhealthy. Spans the failover records and the health check that drives the decision. Covers the active-active alternative, the control plane dependency trap, TTL and transition delay, internal-target health checks, and the static stability pattern.
Does not cover CloudFront-specific failover or general record creation. Those are separate skills.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging, observability). Fall back to the AWS CLI otherwise.
To build region-level failover, follow the procedure exactly. See the Procedure section below.
The procedure covers:
| Choice | Use when |
|---|---|
| Active-passive (failover routing policy) | A primary should serve and a secondary should stand by |
| Active-active (any policy, unhealthy records excluded) | Both Regions should serve traffic at once |
The PRIMARY failover record needs a health signal, or Route 53 always treats it as healthy and never fails over. Pick the mechanism by how the target is reached:
| Choice | Use when |
|---|---|
| Standard endpoint health check | The target is publicly reachable and the customer needs to probe a specific path or signal. Route 53’s public health checkers connect to the endpoint directly |
| ETH (Evaluate Target Health on an alias record) | The target is an alias to a supported AWS resource whose health Route 53 can evaluate (e.g., an ALB/NLB, whether internet-facing or internal, or another in-zone record that has its own health check). No public probe is needed |
| CloudWatch alarm-based health check | The target is a private endpoint that is NOT an alias to a supported resource (e.g., a private EC2/IP behind a standard record). Public health checkers cannot reach it and ETH does not apply, so the health check watches a CloudWatch metric/alarm instead |
| Choice | Use when |
|---|---|
| Health-check-driven | Typical DR |
| Static stability | The workload cannot tolerate any control plane dependency in the failover path |
Constraints:
A long record TTL adds to the transition delay. Lower the TTL deliberately based on the recovery target.
Public health checkers cannot reach a private resource. Use ETH when the target is an alias to a supported AWS resource; use a CloudWatch alarm-based health check for a private endpoint behind a standard record.
The runbook depended on a control plane change in the failed Region. Pre-create records and checks; use static stability for critical paths.
Active-active was built where active-passive was intended. Use the failover routing policy.
This procedure configures active-passive failover for an application in two AWS Regions. It creates a health check on the primary endpoint and primary/secondary failover records, handles internal targets with ETH or a CloudWatch alarm-based health check, sets the TTL against the recovery target, and surfaces the console links to verify.
app.example.com).public): How the failover targets are reached,
which selects the health-check mechanism:
public — publicly reachable endpoint → standard endpoint health checkalias_to_aws_resource — alias to a supported AWS resource (e.g., ALB/NLB, whether
internet-facing or internal) whose health Route 53 evaluates directly → ETH (free, no probe)internal_endpoint — private endpoint behind a standard record (e.g., private EC2/IP), where
ETH does not apply → CloudWatch alarm-based health checktarget_reachability is internal_endpoint):
The CloudWatch alarm that reflects the primary endpoint’s health, and its Region.Constraints for parameter acquisition:
Constraints:
aws sts get-caller-identityaws sts assume-role) rather than long-lived IAM user access keys.Constraints:
Constraints:
If target_reachability is public, create a standard endpoint health check. Match the target
field to {primary_target}: use "IPAddress": "{primary_target}" when the target is an IP, use
"FullyQualifiedDomainName": "{primary_target}" when it is a hostname, or set both together to
probe a specific IP with a Host header. Passing a dotted-decimal IP to
FullyQualifiedDomainName is wrong because Route 53 then tries to DNS-resolve it:
# When primary_target is an IP:
aws route53 create-health-check --caller-reference {ref} --health-check-config '{
"Type": "HTTPS", "IPAddress": "{primary_target}", "Port": 443,
"ResourcePath": "/health", "RequestInterval": 30, "FailureThreshold": 3
}'
# When primary_target is a hostname:
aws route53 create-health-check --caller-reference {ref} --health-check-config '{
"Type": "HTTPS", "FullyQualifiedDomainName": "{primary_target}", "Port": 443,
"ResourcePath": "/health", "RequestInterval": 30, "FailureThreshold": 3
}'If target_reachability is alias_to_aws_resource, do NOT create a standard health check against
the target. Use ETH on the alias record instead (Step 4) — Route 53 evaluates the supported
resource’s own health directly, so a paid standard health check is unnecessary whether the
resource is internet-facing or internal
If target_reachability is internal_endpoint, the target is private and not an alias to a
supported resource, so ETH does not apply. Create a CloudWatch alarm-based health check that
watches a metric reflecting the endpoint’s health, then attach it to the primary record in
Step 4:
aws route53 create-health-check --caller-reference {ref} --health-check-config '{
"Type": "CLOUDWATCH_METRIC",
"AlarmIdentifier": {"Region": "{alarm_region}", "Name": "{alarm_name}"},
"InsufficientDataHealthStatus": "Unhealthy"
}'Set "Unhealthy" because this check drives failover: a data gap should force failover to the
secondary rather than preserve a stale state on the primary.
Constraints:
You MUST create a primary failover record and a secondary failover record with the same name and type:
aws route53 change-resource-record-sets --hosted-zone-id {hosted_zone_id} --change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "{record_name}", "Type": "A",
"SetIdentifier": "primary", "Failover": "PRIMARY",
"TTL": {ttl}, "ResourceRecords": [{"Value": "{primary_target}"}],
"HealthCheckId": "{health_check_id}"
}
}]
}'For an alias_to_aws_resource target, set AliasTarget with EvaluateTargetHealth: true instead
of a HealthCheckId. For public and internal_endpoint targets, attach the health check created
in Step 3 via HealthCheckId
Repeat for the secondary with SetIdentifier: secondary and Failover: SECONDARY
Constraints:
Constraints:
{hostedZoneId} and {healthCheckId}:
Records view:
https://console.aws.amazon.com/route53/v2/hostedzones#ListRecordSets/{hostedZoneId}Health check details:
https://console.aws.amazon.com/route53/v2/healthchecks/home#/details/{healthCheckId}{
"hosted_zone_id": "Z1234567890ABC",
"record_name": "app.example.com",
"primary_target": "192.0.2.1",
"secondary_target": "198.51.100.1",
"ttl": 60
}Created failover records for app.example.com (primary 192.0.2.1, secondary 198.51.100.1)
with health check on the primary.
Verify in the console:
https://console.aws.amazon.com/route53/v2/hostedzones#ListRecordSets/Z1234567890ABC
https://console.aws.amazon.com/route53/v2/healthchecks/home#/details/abcd1234-...Long record TTL adds to the transition delay. Lower the TTL against the recovery target (Step 4).
Public health checkers cannot reach a private resource. Use ETH for an alias to a supported resource, or a CloudWatch alarm-based health check for a private endpoint (Step 3/4).
The runbook depended on a control plane change in the failed Region. Pre-create records and checks; use static stability (Step 5).
Active-active built where active-passive was intended. Use the failover routing policy (Step 2).
aws sts assume-role) rather than
long-lived IAM user access keys, and prefer read-only credentials for inspection steps. Grant
only the specific actions this procedure needs — route53:CreateHealthCheck,
route53:ChangeResourceRecordSets, and route53:GetChange to create the health check and
failover records and confirm propagation, plus route53:ListResourceRecordSets,
route53:GetHostedZone, and route53:GetHealthCheckStatus for inspection — rather than
route53:* or broader service:* wildcards.