When a record you just created in Route 53 refuses to resolve, “waiting for propagation” is usually the wrong diagnosis. Route 53 pushes changes to its authoritative name servers in well under a minute — once the change status reads INSYNC, the record is live at the source. If the name still does not resolve after that, the cause is almost always structural: the registrar is delegating to different name servers than the hosted zone you edited, a duplicate hosted zone exists, or a resolver cached a negative answer before you created the record.
What’s happening
DNS resolution for your domain is a delegation chain: the registrar tells the world which name servers are authoritative (the NS records at the registry), and Route 53 answers queries only if those NS records point at the four awsdns servers assigned to your hosted zone. Every hosted zone gets its own set of four name servers when it is created. Delete a zone and recreate it — or create a second zone with the same name in another account — and you get a different set of four. Records you add to a zone the registry does not delegate to are invisible to the internet, no matter how long you wait.
Two more mechanisms produce “it should work but does not.” First, negative caching: if a resolver asked for the name before you created the record, it cached the NXDOMAIN answer for the duration of the zone’s SOA minimum TTL, and keeps serving “does not exist” until that expires. Second, private hosted zones: a private zone with the same domain name attached to your VPC overrides public resolution inside that VPC, so a record can resolve from your laptop but not from your EC2 instances — or the reverse.
Real propagation delay does exist, but only in one direction: when you change an existing record, remote resolvers keep serving the old value until its TTL expires. That is caching on their side, and nothing on the AWS side can flush it.
What to check first
- Is the change actually applied? The console shows the record;
get-changeshowsINSYNC. If it isPENDING, wait sixty seconds, not a day. - Do the registry NS records match the hosted zone’s NS set? This is the number-one cause, especially after migrations or zone re-creation.
- Is there more than one hosted zone with this name? Check every account the organization owns, and check for a private zone with the same name.
- Does the failure depend on where you test from? Works in the VPC but not outside (or vice versa) points at a private hosted zone or a resolver cache, not the record.
How to verify
Start at the registry, then query Route 53’s own servers directly, bypassing every cache:
# What the world is delegated to
dig +short NS example.com @8.8.8.8
# What the hosted zone thinks its name servers are
aws route53 list-hosted-zones-by-name --dns-name example.com
aws route53 get-hosted-zone --id Z0123456789ABC \
--query 'DelegationSet.NameServers'
If those two lists differ, stop — you found it. If they match, ask an authoritative server directly:
# Bypass all caching: ask Route 53's assigned server, no recursion
dig @ns-123.awsdns-45.com app.example.com A +norec
# Simulate the answer from inside Route 53
aws route53 test-dns-answer --hosted-zone-id Z0123456789ABC \
--record-name app.example.com --record-type A
A correct answer here plus a wrong answer from 8.8.8.8 means caching — either TTL on an old value or negative caching on a name that did not exist yet. Confirm the record itself is what you think it is in Route 53 > Hosted zones > example.com, or:
aws route53 list-resource-record-sets --hosted-zone-id Z0123456789ABC \
--query "ResourceRecordSets[?Name=='app.example.com.']"
Note the trailing dot — Route 53 stores fully qualified names, and forgetting it makes the query return nothing.
The fix
- NS mismatch at the registrar — copy the four name servers from the hosted zone’s NS record and set them at the registrar. If the domain is registered with Route 53 itself, that is Route 53 > Registered domains > example.com > Name servers. Registry NS changes carry their own TTL (often 48 hours at the parent), so this one genuinely takes time.
- Duplicate hosted zones — decide which zone is canonical, move any records worth keeping into it, delete the other. Leaving both invites the next engineer to edit the wrong one again.
- Private hosted zone shadowing — if resolution fails only inside the VPC, look for a private zone with the same name. Add the missing records there too, or remove the VPC association if the private zone is a leftover.
- Negative caching — nothing to fix; the record is fine. Verify against the authoritative server and wait out the SOA minimum TTL. Testing from a resolver that never saw the name (a phone on cellular) confirms it.
- Stale cache after a change — same story: the old TTL has to expire on every resolver that cached it. The lesson goes in the prevention list — lower the TTL before the change, not after.
- Wrong record content — a typo in the name, an A record where an alias was needed, or a record created at the apex instead of
www. If the target is an AWS resource, an alias record is usually the right shape.
When this is the wrong move
If the domain never resolved at all — not even the apex — check the registration before touching DNS: an expired domain or a clientHold status at the registry kills resolution entirely and no hosted zone edit fixes it. And if dig returns the right answer everywhere but the site is down, you have left DNS territory; the problem is the target, not the name. The official Route 53 documentation covers delegation in depth if you want the full chain.
Common mistakes
- Blaming propagation for what is actually an NS mismatch, and waiting days for a fix that will never arrive.
- Recreating a hosted zone and assuming the old name servers still apply — every new zone gets a new set of four.
- Testing repeatedly from the same resolver that cached the negative answer, which resets nothing and proves nothing.
- Editing records in the wrong AWS account because two accounts each hold a zone for the same domain.
Prevention
- Keep exactly one public hosted zone per domain, in one account, and document where it lives.
- Lower TTLs to 60 seconds a day before any planned record change, and raise them back after.
- After any change, confirm
INSYNCwithaws route53 get-change --id <change-id>instead of refreshing a browser. - After any registrar or zone migration, diff the registry NS records against the zone’s delegation set as the final acceptance check.
- For failover setups, pair low TTLs with health checks rather than relying on manual record edits during an incident.
Most “propagation” tickets close in ten minutes once the debugging order is right: registry delegation first, authoritative answer second, caches last. If nobody on your team truly owns the AWS environment end to end, that is the root of most of these problems — Stack Harbor runs AWS accounts daily under AWS Operations, and you can book an AWS review.