Lesson 13 · 20 min read

Route 53: Production Domain

Hosted zones, alias records to your load balancer and CDN, and health-checked routing for a real domain.

What you'll learn in this lesson

  1. Concept: from a long AWS name to yourapp.com
  2. What Lesson 3 gave you, and what is new here
  3. Alias records — the AWS-only record type
  4. Point the domain at the ALB and CloudFront
  5. Routing policies: simple, weighted, latency, failover
  6. Health checks and DNS failover
  7. Private hosted zones — names that only exist inside the VPC
  8. Email records: MX, SPF, DKIM, DMARC
  9. Moving a live domain without downtime
  10. Protecting the domain itself
  11. What this costs
  12. Troubleshooting table
  13. Interview corner
  14. Practice task before Lesson 14
  15. Conclusion

Concept

Your load balancer (an ALB, Application Load Balancer, is an AWS service that shares incoming web requests between several servers) answers at a name like myapp-alb-1234567890.ap-south-1.elb.amazonaws.com. Your CDN (CloudFront is the AWS content delivery network, a service that keeps copies of your files on servers around the world) answers at d1234abcd.cloudfront.net. No customer will ever type either name. This lesson connects your real domain to them with Route 53, the AWS DNS service. DNS (Domain Name System) is the system that turns a name such as yourapp.com into the IP address of a server. We also add features for production: routing that checks health, private internal names, email records, and a way to move a live domain without anyone noticing.

The analogy — the phone book, with a smart operator

Lesson 3 described DNS as the phone book of the internet. Route 53 is a phone book with an operator. The operator can give a different number depending on who is calling, which numbers work right now, and where the caller is. The operator can also point to AWS things by name. When the number behind the name changes, the book updates itself.

What Lesson 3 gave you, and what is new here

If any of these words feels unclear, read Lesson 3: DNS Deep Dive: From Domain Name to Server again first. This lesson builds on it:

  • Hosted zone: the container for all the DNS records of one domain. You already created one and pointed your registrar's nameservers at it. (The registrar is the company where you bought the domain.)
  • Records are the entries in a zone. An A record points a name to an IP address. A CNAME record points a name to another name. An MX record says where to deliver mail. A TXT record holds free text, often used as a proof. An NS record says which servers hold the zone. TTL (time to live) is how many seconds an answer may be saved (cached).
  • Wildcard record: *.yourapp.com answers for any subdomain, so each customer (tenant) can have one.

New in this lesson:

New ideaWhy you need it
Alias recordsPoint the bare domain at an ALB or CloudFront (a CNAME is not allowed there)
Routing policiesSend 10% of users to a new setup, or switch to a backup
Health checksStop giving out the address of something that is down
Private hosted zonesGive the database an internal name that does not change
Email recordsMake mail from your domain reach the inbox, not spam
Cutover procedureMove a live domain with no downtime

Alias records — the AWS-only record type

A load balancer's address is a name, so the obvious record is a CNAME: yourapp.com → myapp-alb-….elb.amazonaws.com. But DNS has a strict rule: a CNAME cannot exist next to any other record with the same name. The bare domain (also called the “apex”, yourapp.com) always has other records. It must have an SOA record (basic facts about the zone) and NS records, and it usually has MX too. So the bare domain cannot be a CNAME.

Route 53 solves this with the Alias record. To the outside world it looks like an A record (or an AAAA record, which is the same thing for IPv6 addresses, the newer, longer kind). But Route 53 works it out inside, and it returns the current IP addresses of the AWS resource that it names.

CNAMEAlias (A / AAAA)
Allowed at the apexNoYes
Can point atAny domain nameCertain AWS resources (ALB, CloudFront, S3 website, API Gateway…) or another record in the same zone
Query costChargedFree for AWS targets
Follows IP changesYes (extra lookup)Yes, automatically, in a single step
TTLYou chooseSet by AWS (60 s for most targets)
Can check target healthNoYes (EvaluateTargetHealth)
Rule of thumb

Do you point to an AWS resource? Use Alias. Do you point to something outside AWS (a SaaS product, your old host)? A subdomain can use a CNAME. The bare domain needs an A record with an IP address.

Point the domain at the ALB and CloudFront

This is what we connect: yourapp.com and *.yourapp.com go to the ALB (the app). assets.yourapp.com goes to CloudFront (Lesson 11).

  1. Create three alias records

    Go to Route 53 → your hosted zone → Create record. For each record, turn on Alias and pick the target from the dropdown list. The console fills in the target's address and its hosted-zone ID for you.

    Record nameTypeAlias toEvaluate target health
    yourapp.comAApplication Load Balancer → myapp-albYes
    *.yourapp.comAApplication Load Balancer → myapp-albYes
    assets.yourapp.comACloudFront distribution (Lesson 11)No
    • The wildcard record does not cover the bare domain, so you create both records.
    • All CloudFront distributions use the same fixed hosted-zone ID. The ID of an ALB is different in each region. So it is better to pick from the dropdown list than to type it.
    • A change goes from PENDING to INSYNC on all Route 53 servers, usually within a minute.
  2. Check it from the outside

    Verify
    • dig +short acme.yourapp.com

      dig is a command-line tool that asks DNS servers questions. Any subdomain should answer through the wildcard with the ALB's IPs. There are several, one per AZ. You never type them, because they can change

    • curl -sI https://yourapp.com

      You should see HTTP/2 200 and a valid certificate. The answer comes from your Auto Scaling group through the ALB

  3. Redirect www to the bare domain (or the reverse)

    Create www as an alias to the ALB as well. Then add one rule on the ALB's HTTPS listener. The rule says: if the host header is www.yourapp.com, redirect (301) to https://yourapp.comand keep the path and query. Search engines treat the two names as separate sites, so pick one as the official name.

Routing policies: simple, weighted, latency, failover

A normal record always gives the same answer. Route 53 can choose the answer depending on conditions. You choose a routing policy for each record.

PolicyWhat it doesTypical use
SimpleOne record gives one answer (or several values, returned in random order)The default. One site, one destination
WeightedSplits traffic by percentage, such as 90 / 10Canary releases (a small test group goes first), moves from an old setup, A/B tests
LatencyAnswers with the AWS region that is fastest for the userThe same app runs in several regions
FailoverThe primary record answers while it is healthy. The secondary takes over if the health check failsA maintenance page, or a standby region
GeolocationAnswers by the user's country or continentContent for legal or regional reasons, sites in different languages
Geoproximity / IP-basedChooses by distance (you can adjust it), or by the caller's IP rangeAdvanced traffic control

Weighted routing — the safest way to migrate

Say you have an old setup (the app on Vercel) and a new one (the ALB). Instead of moving everyone at once, create two records with the same name. Give each one its own record ID (the API calls it SetIdentifier) and a weight. Traffic is split in that ratio:

RecordRecord IDWeightPoints to
yourapp.com Anew-aws-stack10alias → the ALB
yourapp.com Aold-stack90the old host's IP, TTL 60

Start at 10. Watch the error rate and the delay for an hour. Then go to 50, then 100, and then remove the old record. If anything looks wrong, set the weight to 0. A rollback takes as long as your TTL. This is the DNS version of a canary release (a small group of users tries the new version first).

DNS load-splitting is coarse

Weights apply to each DNS answer, not to each request, and resolvers save answers. A big office that uses one resolver all goes to the same side until the TTL ends. The percentages even out across many users, but they are not exact. For exact splits inside one setup, use ALB weighted target groups instead.

Health checks and DNS failover

A Route 53 health check is a robot that runs outside your VPC, in several AWS locations. It requests a URL every 10 or 30 seconds. If enough of these robots see failures, the check turns unhealthy. Any record that is tied to it is then removed from the answers. Your endpoint must be reachable from the internet for this to work.

Create one in Route 53 → Health checks. Use HTTPS, yourapp.com, the path /api/health, an interval of 30 seconds, and unhealthy after 3 failures. Then attach it to a record.

Failover routing uses the health check. You make two records. One is marked PRIMARY (with the health check) and one is marked SECONDARY. While the primary is healthy, everyone gets it. When it fails, DNS switches to the secondary. A common cheap secondary is a simple “we'll be right back” page in S3 (the AWS file storage service) behind CloudFront. The site shows a simple page instead of failing completely, and it costs very little.

What DNS failover cannot do

It is not instant. Detection takes about 90 seconds (3 failures × 30 s). Then the saved answers must also expire (the TTL). So expect two to five minutes. Fast failover inside one region is the ALB's job (it takes seconds). Use Route 53 failover when a whole setup or a whole region fails, not for one sick server. And test it: switch off the primary and time the change. A failover that you have not tested is only a hope, not a plan.

Private hosted zones — names that only exist inside the VPC

A private hosted zone is a zone that is attached to a VPC. Its records answer only to resources inside that VPC, and the internet cannot see them. It solves a problem from Lesson 8. The RDS hostname (myapp-db.abc123xyz.ap-south-1.rds.amazonaws.com) is pasted into .env and SSM. When you restore a snapshot or the database fails over, the hostname changes, and you must edit every config.

The fix is to give the database a name that you control.

Create a private hosted zone internal.yourapp.com and attach it to the myapp VPC. Add one CNAME in it: db.internal.yourapp.com points to the RDS endpoint, with a TTL of 60. The app's connection string now uses @db.internal.yourapp.com:5432.

Do you restore a snapshot? Update one record instead of the config of every server. The VPC needs the settings enableDnsHostnames and enableDnsSupport (we set the first one in Lesson 6). There is one catch. The RDS certificate contains the AWS hostname, not your custom name. If your database driver checks that the name matches, the check fails. With sslmode=require the traffic is still encrypted, but the name is not checked. Or you can give the driver the AWS name as the TLS server name.

Email records: MX, SPF, DKIM, DMARC

When your domain sends email (welcome messages, password resets), DNS decides whether the mail reaches the inbox or the spam folder. The MX record says where to deliver mail that is sent to you. Three more records prove that mail that claims to come from yourapp.com is real. SPF is a record that lists the servers allowed to send mail for your domain. DKIM is a record that holds a public key, so receivers can check that a message was signed by you. DMARC is a record that tells receivers what to do with mail that fails SPF or DKIM. Amazon SES is the AWS service for sending email:

RecordSaysExample
MXWhere to deliver mail that is sent to you10 inbound-smtp.ap-south-1.amazonaws.com or your Google/Zoho mail servers
SPF (TXT at the apex)Which servers may send mail as your domain"v=spf1 include:amazonses.com ~all"
DKIM (CNAMEs from your sender)A public key, so receivers can check the digital signature of each messagethree CNAMEs that SES (or your mail provider) gives you
DMARC (TXT at _dmarc)What to do when SPF or DKIM checks fail, and where to send reports"v=DMARC1; p=none; rua=mailto:dmarc@yourapp.com"
The safe order

Start DMARC with p=none (only watch). Read the reports for a few weeks to find every sender that is allowed. Then change to quarantine (send failing mail to spam) and finally to reject (refuse failing mail). If you jump straight to reject, your own invoices can disappear without a sign. Amazon SES and similar services give you the exact records to paste. Your job is to know where they go and why.

Moving a live domain without downtime

Here is the situation. The site runs on Vercel (or an old host) and you are moving it to this AWS setup. DNS is where downtime can sneak in, because old answers stay in caches. A runbook (a list of steps to follow) for this move looks like this:

  1. Two days before: copy everything and lower the TTL

    Export the existing zone. Recreate every record type in the Route 53 hosted zone: MX, TXT, and verification CNAMEs, not only the A record. Then lower the TTL of the records that will change to 60 seconds. Wait at least as long as the old TTL, so that the change reaches everywhere. This one step makes the switch fast and easy to undo.

  2. Test the new setup for real before anyone is sent to it

    Bypass DNS and talk to the ALB directly
    • curl -sI --resolve yourapp.com:443:<ALB-IP> https://yourapp.com/

      Sends the request for yourapp.com to one of the ALB's IPs, and the certificate still checks out. Test login, uploads and every important page. No DNS change is needed

  3. Cut over — weighted first if you can

    Change the nameservers at the registrar to the four Route 53 NS values only if the zone is completely ready. If it is not, change only the records. Use the weighted move from above (10, then 50, then 100), or switch the record at a quiet hour.

  4. Watch, keep the old stack, then raise the TTL

    Keep the old host running for at least the old TTL plus one day. Users behind resolvers with old saved answers still reach it. Watch the 5xx rate and the delay on the ALB. When all is stable, raise the TTL again to 300–3600 seconds. This lowers query costs and speeds up lookups. Only then turn off the old setup.

The TTL trade-off in one line

A low TTL means changes work fast, but there are more queries and the first lookups are a little slower. A high TTL is the opposite. Use a high TTL most of the time and a low TTL around changes.

Protecting the domain itself

A perfect setup is worth nothing if someone else controls the domain. Real incidents come from the boring parts:

  • Expiry. Turn on auto-renew and keep a valid payment method. Companies have lost domains because a card expired. Also put the expiry date in a calendar.
  • Security of the registrar account. The account that owns the domain has the highest power. Use MFA (a second login step, such as a code from an app). Use a shared team mailbox, not one person's address, as the contact. Do not reuse passwords. Whoever controls this account controls all your DNS.
  • Domain lock and privacy. Turn on the transfer lock, which stops anyone from moving the domain away. WHOIS privacy hides your personal details from the public registration database. Route 53 Domains includes it for free on most TLDs (a TLD is the ending, such as .com).
  • Dangling records. A dangling record points to something that no longer exists. For example, it may point to an S3 bucket, an ELB or a third-party service that you deleted. Someone else can claim that name and take over your subdomain. This is called a “subdomain takeover”. Delete the DNS record when you delete the resource behind it.
  • CAA record. This is a short record that limits which certificate authorities (the companies that issue certificates) may issue certificates for your domain. Examples: 0 issue "amazon.com" and 0 issue "letsencrypt.org".
  • DNSSEC adds digital signatures to your DNS answers, so nobody can fake them. Route 53 supports it. But it adds risk: one mistake with a key can make the domain stop working. Turn it on on purpose, after you understand it. Do not turn it on just because it exists.

What this costs

ItemApprox. price
Hosted zone (public or private)$0.50 per month each
Standard queries$0.40 per million
Alias queries to ALB / CloudFront / S3Free
Health check (basic)~$0.50–$2.00 per month each, depending on options (HTTPS and fast checks cost extra)
Domain registrationDepends on the TLD: .com is about $14–15 a year. Check the current price for others

The whole DNS part of this project costs roughly $1–3 a month plus the domain fee. It is the cheapest part of the setup, and it gives you a lot of control.

Troubleshooting table

SymptomLikely causeFix
dig shows the old IP hours after the changeDNS resolvers (the servers that look up names for users) still keep the old answer until its old TTL endsYou should have lowered the TTL a day earlier. Run dig @8.8.8.8 and dig @ns-xxx.awsdns-xx.com to see the real (authoritative) answer
Works on your phone, not on your laptopYour laptop or your internet provider's resolver has a saved answer (or a saved “does not exist” answer)Clear the cache: sudo dscacheutil -flushcache and sudo killall -HUP mDNSResponder (macOS), or ipconfig /flushdns (Windows). Or test with another resolver
Cannot create a CNAME at the bare domainDNS does not allow a CNAME next to the other records that every bare domain has (SOA, NS)Use an alias A record (Route 53) on the bare domain. Use a CNAME only for subdomains
Certificate error on the new domainThe ACM certificate does not cover that name, is not validated, or (for CloudFront) is not in us-east-1Check the certificate's domain list and status. A wildcard does not cover the bare domain
ERR_NAME_NOT_RESOLVED for a whole domainThe nameservers at the registrar do not match the four NS values of the hosted zone, or the domain has expiredCompare the registrar's NS list with the NS record of the hosted zone. Check the expiry date
The bare domain works, www does not (or the other way round)Only one of the two records existsCreate both. Pick one as the official address and redirect the other to it
Weighted routing sends everything to one sideA weight is 0, a health check is failing, or resolvers keep the old answer (caching)Check the weight, the record ID and the attached health check of each record
Email stopped working after the DNS moveThe MX, SPF and DKIM records were not copied to the new hosted zoneExport the old zone before you move. Recreate every record type, not only A

Two lookups that solve most DNS problems

Debugging DNS
  • dig NS yourapp.com +short

    Which nameservers does the world think are in charge? The answer must equal the NS record of the hosted zone. A mismatch is the #1 cause of “my records don't work”

  • dig @ns-123.awsdns-45.org yourapp.com

    Ask Route 53 directly. You get the real answer and no cache is used. If this answer is right but users still see old answers, the cause is the TTL. Wait

Use these together with dig +trace and a question to a public resolver (Lesson 3). They solve almost every DNS problem.

Interview corner

Why can't you put a CNAME on the root domain, and what does Route 53 do instead?

A CNAME cannot exist next to other records with the same name, and the bare domain must have SOA and NS records (and often MX). Route 53 offers alias records instead. They look like A or AAAA records, and Route 53 works out the current IPs of the AWS resource inside. They are allowed on the bare domain, and queries to them are free.

How would you migrate a production site to a new host with no downtime?

I recreate all records and lower the TTLs well before the move. I test the new setup directly with curl --resolve. I move traffic step by step with weighted records and watch the results. I keep the old setup running for at least the old TTL. Then I raise the TTLs again and switch the old setup off.

What is a private hosted zone used for?

It holds internal names that work only inside the VPCs that are attached to it, such as db.internal.example.com. Applications no longer depend on endpoint names that AWS generates, and the internal layout stays out of public DNS.

Explain Route 53 failover routing and its limits.

There is a primary and a secondary record, with a health check on the primary. If the check fails, Route 53 answers with the secondary. The time it needs is the detection time plus the TTL of the saved answers. That means minutes, not seconds. So it suits the failure of a whole site or region, not a problem with one server.

Users in one office still reach the old server after your DNS change. Why?

Their resolver keeps the old answer until the previous TTL ends (some resolvers also ignore low TTLs). I ask the authoritative server (the nameserver that holds the real records, not a cache) to confirm that the change is live. For future changes, I lower the TTL beforehand.

What are SPF, DKIM and DMARC?

SPF lists the servers that may send mail for the domain. DKIM signs messages with a key that is published in DNS. DMARC sets the policy (none, quarantine or reject) and the reports for mail that fails those checks. Together they stop faked mail (spoofing) and help mail reach the inbox.

What is a subdomain takeover?

A DNS record still points to a cloud resource that was deleted (a bucket, an app, an ELB). An attacker claims that resource name and can then serve content on your subdomain. You prevent it by deleting DNS records together with the resources they point to.


Practice task before Lesson 14

  1. Create alias records for the apex, * and www pointing at the ALB, and assets pointing at CloudFront. Verify each with dig and curl.
  2. Add the ALB rule that redirects www to the bare domain and confirm the 301 with curl -I.
  3. Create two weighted records for a test subdomain (canary.yourapp.com). Point one to the ALB and one to a plain IP, with weights 50/50. Look it up many times in a row and watch the split (and the caching).
  4. Create a Route 53 health check on /api/health. Stop the app on every server (or temporarily change the target group path), and watch the health check turn unhealthy in the console.
  5. Create the private hosted zone, add db.internal.yourapp.com, and from an EC2 server run dig +short db.internal.yourapp.com. From your laptop the same command should return nothing.
  6. Write down the cutover runbook for your own domain in six bullet points. Include the TTL you would set and when.
Optional stretch

Add a CAA record and an SPF + DMARC (p=none) record. Then check the result with a public tool such as mxtoolbox.com or Google's Admin Toolbox.

Conclusion

Route 53 is where the long AWS names become your own brand. It also gives you safe tools for changing things in production.

  • Alias records for anything in AWS, including the bare domain, where a CNAME is not allowed.
  • Routing policies turn DNS into a traffic tool: weighted records for step by step moves, failover for outages of a whole site.
  • Private zones give internal resources names that stay the same after restores and failovers.
  • Email needs its own records (MX, SPF, DKIM, DMARC), and every record type must move with the zone.
  • TTL is your rollback speed. Keep it low around changes and high the rest of the time. Test with --resolve before you switch.

The whole setup is now live on your own domain. But you built it by hand, and you deploy by logging in over SSH. Now it is time to automate the release with CI/CD.


End of Lesson 13. Next: Lesson 14 — CI/CD with GitHub Actions.

Published by Jainik · Last updated · How these lessons are made

Keep learning

Related series