Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 052 / 100

Route 53 DNS: a practical AWS guide for working engineers

DNS that integrates with everything on AWS.

Expona Admin8 min read

Route 53 DNS is AWS's managed Domain Name System service, and after five weeks of building infrastructure, you're ready to understand the glue that holds everything together. Whether you're pointing users to a load balancer, distributing traffic geographically, or managing failover scenarios, Route 53 is where DNS policy becomes application strategy. In this post, we'll build on what we've learned about load balancers and CDNs by exploring how Route 53 routes traffic and connects your domain to AWS resources.

Hosted Zones — Public for the Internet, Private for VPCs

A hosted zone is your DNS namespace in Route 53. Think of it as a container for all the DNS records that define how traffic reaches your domain. AWS gives you two types: public and private, each suited to different visibility needs.

Public hosted zones are what most applications need. They're queryable from anywhere on the internet. When someone types your domain into a browser, their DNS resolver queries the public hosted zone to get the IP address or alias target. You create a public hosted zone when you register a domain through Route 53, or when you point an existing domain's nameservers to Route 53's nameservers.

I worked on a travel platform that started with a single public hosted zone for travelapp.com. The zone contained A records for the web app, CNAME records for subdomains, and MX records for email. Every DNS lookup from any user, anywhere, was resolved from this single public zone.

Private hosted zones are confined to one or more VPCs. They're not queryable from the internet; only EC2 instances and containers within your VPC can resolve names in the private zone. This is invaluable for internal service discovery, especially in microservices architectures. I've used private zones for databases, internal APIs, and cache layers that should never be accessible from the public internet.

Here's a practical scenario: your VPC has an internal API service running on api.internal.example.com. Create a private hosted zone for internal.example.com, add an A record pointing api.internal.example.com to your API's network load balancer, and every container in that VPC automatically knows how to reach it by name. No hardcoded IPs, no service discovery framework overhead.

To create a private hosted zone via AWS CLI:

bash
aws route53 create-hosted-zone \
  --name internal.example.com \
  --vpc VPCRegion=us-east-1,VPCId=vpc-12345678 \
  --caller-reference "$(date +%s)"

One crucial detail: private hosted zones require VPC DNS resolution enabled. By default it's on, but if you've disabled enableDnsHostnames or enableDnsSupport on your VPC, the zone won't resolve.

Alias Records — Point Root Domains to Load Balancers and CloudFront

In traditional DNS, you use A records for IPv4 and AAAA records for IPv6. Route 53 adds a AWS-specific concept called alias records, which are powerful and cost-efficient.

An alias record is a Route 53 extension that maps a domain name to another AWS resource—an Application Load Balancer, Network Load Balancer, CloudFront distribution, S3 bucket, API Gateway, or even another Route 53 record. Unlike standard A records, alias records are evaluated at query time, not stored as static IPs. This means if your load balancer's IP changes (which it shouldn't, but Route 53 handles it transparently), your DNS stays in sync.

Why this matters for root domains: the root domain (e.g., example.com without a subdomain prefix) cannot be a CNAME record in standard DNS. CNAMEs can only exist on subdomains. This used to force developers into two choices: point the root to an A record with a static IP (fragile), or use a CNAME at the root (technically violates DNS standards). Route 53's alias records solve this elegantly. You can alias example.com directly to your CloudFront distribution or load balancer.

On the e-commerce platform I built, the root domain shop.example.com was aliased to an Application Load Balancer. Any HTTPS traffic to the root was instantly routed to the ALB, which distributed it to backend servers. The alias automatically stayed current without any IP management from us.

Here's how to create an alias record pointing a root domain to an ALB, using the AWS CLI:

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

Notice EvaluateTargetHealth. When set to true, Route 53 checks the health of the ALB before returning it as a DNS answer. If the ALB is unhealthy, Route 53 won't direct traffic to it, providing a simple failover mechanism without a separate health check resource.

Alias records also have no additional cost, while standard DNS queries incurred charges in older versions of Route 53. This makes them the preferred approach whenever you're pointing to AWS resources.

Routing Policies — Latency, Weighted, Geolocation, and Failover

Route 53 supports multiple routing policies, each answering the question: "How should I distribute traffic?" differently.

Simple routing is the baseline. One domain name, one resource. When a user queries, they get that resource. No intelligence, no distribution.

Weighted routing lets you assign a weight to each record. Route 53 distributes queries proportionally based on weight. On a ride-hailing platform I worked on, we used weighted routing to gradually shift traffic during a deployment. We'd set the old version to weight 90 and the new version to weight 10, monitoring errors in the new version before ramping it up to 50-50, then 0-100.

bash
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "api-v1",
          "Weight": 90,
          "AliasTarget": {
            "HostedZoneId": "Z35SXDOTRQ7X7K",
            "DNSName": "v1-alb.us-east-1.elb.amazonaws.com",
            "EvaluateTargetHealth": true
          }
        }
      },
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "api-v2",
          "Weight": 10,
          "AliasTarget": {
            "HostedZoneId": "Z35SXDOTRQ7X7K",
            "DNSName": "v2-alb.us-east-1.elb.amazonaws.com",
            "EvaluateTargetHealth": true
          }
        }
      }
    ]
  }'

Latency-based routing is powerful for global applications. You create multiple records with the same name, each pointing to a resource in a different AWS region. When a user queries, Route 53 responds with the record from the region with the lowest latency to them. This isn't real-time latency measurement; Route 53 uses CloudWatch metrics and historical data to make routing decisions. On the travel booking platform, we deployed the web app in us-east-1, eu-west-1, and ap-southeast-1, each with latency-based routing. Users in Asia automatically hit the Singapore region, European users hit Ireland, and US users hit Virginia.

Geolocation routing routes based on the geographic location of the DNS query originator, not latency. You specify a country, continent, or set a default. This is useful for compliance (route EU traffic to EU servers) or localized content. On a SaaS platform with data residency requirements, geolocation routing ensured UK customers hit UK data centers.

Failover routing designates one record as primary and another as secondary. Route 53 monitors the primary (via a health check) and automatically switches to secondary if the primary fails. This is different from weighted or latency routing; there's no distribution—only failover.

bash
aws route53 create-health-check \
  --health-check-config '{
    "Type": "HTTPS",
    "ResourcePath": "/health",
    "FullyQualifiedDomainName": "primary-api.example.com",
    "Port": 443,
    "RequestInterval": 30,
    "FailureThreshold": 3
  }'

Once your health check is created, reference it in the primary failover record. If the health check fails three consecutive times (or whatever threshold you set), Route 53 switches DNS answers to the secondary record.

Domain Registration — Register and Renew Domains in the Same Place

Route 53 isn't just DNS hosting; it's also a domain registrar. You can register new domains directly through Route 53 and manage them alongside your hosted zones. For smaller projects or when you want everything in one AWS account, this consolidation is convenient.

When you register a domain through Route 53, it automatically creates a public hosted zone and sets the nameservers. If you already own a domain elsewhere (GoDaddy, Namecheap, etc.), you can create a Route 53 hosted zone and point your domain's nameservers to Route 53's nameservers.

I typically register domains through Route 53 for new projects because it's frictionless—create the domain, and the hosted zone is ready. For existing domains, I point their nameservers to Route 53 without migrating the domain registration itself.

Renewal is straightforward. Route 53 sends renewal notices before expiration, and you can auto-renew or renew manually. Renewal costs are consistent with other registrars, around $10-15 per year for most TLDs.

One detail: if you delete a hosted zone, the domain's DNS stops resolving immediately (unless you've changed nameservers elsewhere). Be intentional about zone deletion, especially in production.

Common Mistakes

  • Forgetting to enable VPC DNS resolution: Private hosted zones won't work if enableDnsHostnames is disabled on your VPC. Check this before troubleshooting for hours.
  • Using CNAME for root domains: Some developers still try to create a CNAME at the root. Use an alias record instead. Route 53 prevents this mistake by defaulting to A/AAAA for the apex, but it's worth knowing why.
  • Not setting EvaluateTargetHealth: If your load balancer or resource becomes unhealthy, Route 53 will still serve its DNS answer unless you enable health evaluation. Always set this for production records pointing to resources you want to fail over.
  • Mixing routing policies carelessly: You can't combine weighted and geolocation policies on the same record. Choose one policy and build your distribution logic around it.
  • Ignoring TTL implications: A low TTL (Time To Live) means DNS changes propagate faster, but clients query more often. A high TTL reduces queries but slows propagation. 300 seconds is a good middle ground for most applications.

Key Takeaways

  • Public hosted zones resolve names from the internet; private hosted zones are confined to VPCs and ideal for internal service discovery.
  • Alias records map AWS resources (load balancers, CloudFront, S3) to domain names without static IPs, and they're the standard approach for root domain routing.
  • Weighted routing is perfect for canary deployments and gradual traffic shifts; latency-based routing optimizes for global performance; geolocation routing enforces compliance; failover routing provides simple active-passive redundancy.
  • Route 53 combines DNS hosting and domain registration in one place, simplifying domain lifecycle management within your AWS account.
  • Always enable health checks on failover records and EvaluateTargetHealth on alias records to ensure automatic failover works as intended.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles

Route 53 DNS | Developer Guide by Expona Engineering