# Fix AWS Route 53 Resolution Failed

DNS resolution is failing for your domain. Users can't reach your application, or you're getting inconsistent results when resolving your domain name. Route 53 is AWS's DNS service, and when it doesn't work correctly, everything downstream fails. The issues can range from missing records to health check failures, delegation problems, or routing policy misconfigurations.

Diagnosis Commands

First, check if the hosted zone exists and is properly configured:

bash
aws route53 list-hosted-zones \
  --query 'HostedZones[*].[Id,Name,Config.Comment]'

Get details about a specific hosted zone:

bash
aws route53 get-hosted-zone \
  --id Z1234567890ABC \
  --query '[HostedZone.Id,HostedZone.Name,HostedZone.Config,DelegationSet.NameServers]'

List all records in the zone:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[*].[Name,Type,Value,TTL]'

Test DNS resolution from different sources:

```bash # Using dig dig mydomain.com

# Using nslookup nslookup mydomain.com

# Using AWS CLI with a specific resolver dig @8.8.8.8 mydomain.com dig @ns-1234.awsdns-12.com mydomain.com ```

Check if the domain is registered with Route 53:

bash
aws route53domains list-domains \
  --query 'Domains[*].[DomainName,Status,ExpirationDate]'

Get domain registration details:

bash
aws route53domains get-domain-detail \
  --domain-name mydomain.com \
  --query '[DomainName,Status,Nameservers[*].Name]'

Check health checks associated with records:

bash
aws route53 list-health-checks \
  --query 'HealthChecks[*].[Id,CallerReference,HealthCheckConfig.Type,HealthCheckConfig.FullyQualifiedDomainName]'

Get health check status:

bash
aws route53 get-health-check-status \
  --health-check-id abc12345 \
  --query 'HealthCheckObservations[*].[IPAddress,Status)'

Check traffic policies if using them:

bash
aws route53 list-traffic-policies \
  --query 'TrafficPolicies[*].[Id,Name,Type,Version]'

Common Causes and Solutions

Missing DNS Record

The record simply doesn't exist in Route 53:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Name==`mydomain.com.`]'

Create the missing record:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "mydomain.com",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{"Value": "192.0.2.1"}]
      }
    }]
  }'

For alias records pointing to AWS resources:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "mydomain.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "Z2158YMRK4210X",
          "DNSName": "my-alb-123456.us-east-1.elb.amazonaws.com",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

Delegation Not Set Up

Your domain registrar doesn't point to Route 53's nameservers:

Get Route 53 delegation set nameservers:

bash
aws route53 get-hosted-zone \
  --id Z1234567890ABC \
  --query 'DelegationSet.NameServers'

These should match the nameservers at your registrar:

```bash # For Route 53 registered domains aws route53domains get-domain-detail \ --domain-name mydomain.com \ --query 'Nameservers[*].Name'

# For external registrars, check their DNS management interface # Common nameserver check dig mydomain.com NS +short ```

If they don't match, update at your registrar. For Route 53 registered domains:

bash
aws route53domains update-domain-nameservers \
  --domain-name mydomain.com \
  --nameservers Name=ns-1234.awsdns-12.com,Name=ns-5678.awsdns-34.com

For external registrars (like GoDaddy, Namecheap), log into their dashboard and update the nameservers to Route 53's values.

Delegation changes can take 24-48 hours to propagate.

Health Check Failing on Failover Record

For failover routing, records are only returned if health checks pass:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Type==`A` && HealthCheckId!=`null`].[Name,HealthCheckId,Failover]'

Check the health check status:

bash
aws route53 get-health-check-status \
  --health-check-id abc12345

If health check is failing, the failover record won't be returned in DNS queries. Fix the endpoint:

```bash # Test the endpoint directly curl -v http://endpoint-server/health

# Check what the health check is monitoring aws route53 get-health-check \ --health-check-id abc12345 \ --query 'HealthCheckConfig' ```

If the endpoint is healthy but Route 53 thinks it's unhealthy, check: - Health check path returns 200 OK - Health check port is correct - HTTPS certificate is valid (for HTTPS health checks)

Update health check configuration:

bash
aws route53 update-health-check \
  --health-check-id abc12345 \
  --resource-path /health \
  --port 8080

Wrong Record Type

Common mistake: using A record for domain when you need alias:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Name==`www.mydomain.com.`]'

Alias records are special Route 53 records that point to AWS resources. They have different behavior than regular A records.

To create an alias:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "www.mydomain.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "Z2FDTNDATAQYW2",
          "DNSName": "d12345.cloudfront.net",
          "EvaluateTargetHealth": false
        }
      }
    }]
  }'

Geolocation Routing Issues

Geolocation records not resolving correctly:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Type==`A` && GeoLocation!=`null`].[Name,GeoLocation]'

Geolocation is based on the resolver's IP, not the client's IP. If you're using a public DNS resolver like Google (8.8.8.8), geolocation uses Google's location, not yours.

Test from different locations:

```bash # Test from Google DNS (US-based) dig @8.8.8.8 mydomain.com +short

# Test from Cloudflare DNS dig @1.1.1.1 mydomain.com +short ```

Add a default record that covers unmatched locations:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "mydomain.com",
        "Type": "A",
        "GeoLocation": {"CountryCode": "*"},
        "TTL": 300,
        "ResourceRecords": [{"Value": "192.0.2.50"}]
      }
    }]
  }'

Latency Routing Issues

Latency routing not returning nearest endpoint:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Type==`A` && Region!=`null`].[Name,Region,Value]'

Latency routing is based on AWS's measurements between the resolver's region and your endpoints. It's not real-time network latency.

Make sure you have records in regions matching your infrastructure:

bash
# Add latency records for each region
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "api.mydomain.com",
          "Type": "A",
          "Region": "us-east-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "1.2.3.4"}]
        }
      },
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "api.mydomain.com",
          "Type": "A",
          "Region": "eu-west-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "5.6.7.8"}]
        }
      }
    ]
  }'

Weighted Routing Not Distributing

Weighted routing not distributing as expected:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Type==`A` && Weight!=`null`].[Name,Weight,Value]'

Weights are relative, not percentages. If you have weights 10, 20, 30, the distribution is 10:20:30 (16.7%:33.3%:50%), not 10%:20%:30%.

Adjust weights:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "mydomain.com",
          "Type": "A",
          "Weight": 50,
          "SetIdentifier": "server1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "1.2.3.4"}]
        }
      },
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "mydomain.com",
          "Type": "A",
          "Weight": 50,
          "SetIdentifier": "server2",
          "TTL": 60,
          "ResourceRecords": [{"Value": "5.6.7.8"}]
        }
      }
    ]
  }'

TTL Issues

High TTL causing stale records:

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[*].[Name,TTL]'

For records that change frequently (like failover), use lower TTL:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "mydomain.com",
        "Type": "A",
        "TTL": 60,
        "ResourceRecords": [{"Value": "192.0.2.1"}]
      }
    }]
  }'

CNAME at Apex

CNAME records cannot exist at zone apex (the domain name itself):

bash
aws route53 list-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --query 'ResourceRecordSets[?Name==`mydomain.com.` && Type==`CNAME`]'

If you find a CNAME at apex, replace it with an alias record:

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [
      {
        "Action": "DELETE",
        "ResourceRecordSet": {
          "Name": "mydomain.com",
          "Type": "CNAME",
          "TTL": 300,
          "ResourceRecords": [{"Value": "something.com"}]
        }
      },
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "mydomain.com",
          "Type": "A",
          "AliasTarget": {
            "HostedZoneId": "Z2158YMRK4210X",
            "DNSName": "my-alb.elb.amazonaws.com",
            "EvaluateTargetHealth": true
          }
        }
      }
    ]
  }'

Verification Steps

Test resolution after changes:

```bash # Test basic resolution dig mydomain.com +short

# Test with specific nameserver dig @ns-123.awsdns-12.com mydomain.com

# Test all nameservers for ns in $(aws route53 get-hosted-zone --id Z1234567890ABC --query 'DelegationSet.NameServers' --output text); do echo "Testing $ns:" dig @$ns mydomain.com +short done

# Verify health checks aws route53 get-health-check-status --health-check-id abc12345 ```

Check DNS propagation:

bash
# Use online DNS propagation checker or multiple resolvers
dig @8.8.8.8 mydomain.com
dig @1.1.1.1 mydomain.com
dig @9.9.9.9 mydomain.com

Create DNS monitoring:

```bash # Create health check that monitors your domain resolution aws route53 create-health-check \ --caller-reference "dns-monitor-$(date +%s)" \ --health-check-config '{ "Type": "HTTPS", "FullyQualifiedDomainName": "mydomain.com", "ResourcePath": "/health", "RequestInterval": 30, "FailureThreshold": 3 }'

# Create CloudWatch alarm for DNS resolution aws cloudwatch put-metric-alarm \ --alarm-name dns-resolution-failure \ --alarm-description "DNS health check failing" \ --namespace AWS/Route53 \ --metric-name HealthCheckStatus \ --dimensions Name=HealthCheckId,Value=abc12345 \ --statistic Minimum \ --period 60 \ --threshold 1 \ --comparison-operator LessThanThreshold \ --evaluation-periods 2 \ --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts ```

DNS diagnostic script:

```bash #!/bin/bash ZONE_ID="Z1234567890ABC" DOMAIN="mydomain.com"

echo "Route 53 DNS Diagnostics" echo "========================"

echo "1. Hosted Zone Info:" aws route53 get-hosted-zone \ --id $ZONE_ID \ --query '[HostedZone.Name,DelegationSet.NameServers]'

echo "" echo "2. DNS Records:" aws route53 list-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --max-items 10 \ --query 'ResourceRecordSets[*].[Name,Type,Value,AliasTarget.DNSName]'

echo "" echo "3. Resolution Test:" echo "Using Google DNS (8.8.8.8):" dig @8.8.8.8 $DOMAIN +short

echo "" echo "Using Route 53 nameserver:" NS=$(aws route53 get-hosted-zone --id $ZONE_ID --query 'DelegationSet.NameServers[0]' --output text) dig @$NS $DOMAIN +short

echo "" echo "4. Health Checks:" aws route53 list-health-checks \ --query 'HealthChecks[*].[Id,HealthCheckConfig.FullyQualifiedDomainName,HealthCheckConfig.ResourcePath]'

echo "" echo "5. Health Check Status:" for hc in $(aws route53 list-health-checks --query 'HealthChecks[*].Id' --output text); do echo "Health Check $hc:" aws route53 get-health-check-status --health-check-id $hc --query 'HealthCheckObservations[*].Status' done ```