The server died at 14:02. A new one was up at 14:20, and the A record was updated at 14:21. Half the users were still hitting the dead IP at 16:00. The fix took a minute. The TTL was 86400.
DNS changes don't "propagate". Resolvers cache your old answer for as long as you told them they could, and you told them a day.
TL;DR
- TTL is a promise: "you may cache this answer for N seconds". Resolvers keep that promise even when you change your mind.
- Lowering a TTL only helps after the old TTL has expired. Lower it at least one old-TTL ahead of any planned change.
- 300 seconds is a sane default for records you might need to move. 3600+ is fine for records that never change.
- My opinion: "DNS propagation takes 24 to 48 hours" is mostly a myth caused by long TTLs people set themselves.
How the caching actually works
When a user's resolver looks up your domain, it gets the record plus a TTL. It serves that answer from cache until the TTL counts down to zero, then asks again.
You can watch the countdown:
$ dig example.com A @1.1.1.1 +noall +answer example.com. 2841 IN A 203.0.113.10 $ sleep 10; dig example.com A @1.1.1.1 +noall +answer example.com. 2831 IN A 203.0.113.10 That 2841 is how many seconds Cloudflare's resolver will keep handing out the old IP, no matter what you change at your DNS host.
Compare with what your authoritative server says right now:
dig +short NS example.com dig example.com A @ns1.your-dns-host.com +noall +answer If the authoritative answer is new and the resolver answer is old, there's nothing to fix. You're waiting out a cache.
The trap: lowering TTL during the incident
This is the mistake almost everyone makes once. The site is down, you change the record and drop the TTL to 60. It doesn't help. Resolvers that cached the old record with a TTL of 86400 won't come back to see your new TTL for up to a day.
TTL changes apply going forward. They don't reach into caches that already exist.
The 48-hour rule for planned moves
For any planned IP change, migration or provider switch:
- T minus 2 days (or at least one full old TTL): lower the TTL to 300 or 60.
- Wait until the old TTL has fully expired everywhere.
- Make the change. Now the worst-case stale period is 5 minutes.
- Keep the old server answering (or redirecting) for a while anyway. Some clients ignore TTLs.
- Raise the TTL again once you're sure.
A script to check what several big public resolvers see, so you know when you're actually done:
#!/usr/bin/env bash NAME=${1:-example.com} for r in 1.1.1.1 8.8.8.8 9.9.9.9 208.67.222.222; do printf '%-16s ' "$r" dig "$NAME" A @"$r" +noall +answer | awk '{print $2, $5}' | tr '\n' ' ' echo done Output like 1.1.1.1 212 203.0.113.20 tells you both the answer and how long it will stay cached.
The clients that ignore you
Some layers keep their own DNS cache regardless of TTL:
- JVM apps historically cached lookups forever unless
networkaddress.cache.ttlwas set. - Long-lived connection pools never re-resolve. They keep talking to the old IP until the connection breaks.
- Browsers keep a short internal cache, and the OS has its own.
That's why step 4 matters. If you're helping a user who is stuck on the old address, How to flush DNS cache on Windows 11, macOS, Linux, Chrome and Android has the commands for every platform.
Negative caching: the other TTL
If someone looks up a name before it exists, resolvers cache the "no such domain" answer too. The duration comes from the SOA record's minimum field:
dig example.com SOA +noall +answer The last number in the SOA is the negative cache TTL. If it's 3600 and you create a new subdomain a minute after someone queried it, that person sees NXDOMAIN for up to an hour. That's a common cause of "it works for me but not for the client" during launches. The browser version of that error is covered in How to fix DNS_PROBE_FINISHED_NXDOMAIN.
So what should you set?
| Record | Suggested TTL | Why |
|---|---|---|
| A/AAAA for web, might move | 300 | 5-minute worst case on failover |
| A/AAAA behind a CDN (CNAME to CDN) | let the CDN decide | they manage the edge IPs |
| MX, TXT (SPF, verification) | 3600 | rarely change, fewer queries |
| NS | 86400 | almost never change |
| Anything during a planned move | 60 | temporary |
Low TTLs cost a little more resolver traffic and a few ms on cold lookups. For most sites that's a cheap insurance premium.
How to tell it's DNS and not the server
When users report the site is down after a change, check what they resolve. If their IP is the old one, it's cache. If it's the new one and the request still fails, it's the server. A neutral outside view such as upordown.online helps here, because it isn't sitting behind your office resolver's cache. It also helps to be strict about what counts as "up", which is why ping alone isn't enough to answer that question.
My take
Long TTLs aren't wrong, but long TTLs on records you might need to change in a hurry are a decision to accept long outages. Most teams never made that decision on purpose. The registrar's default made it for them.
Over to you
What TTL do you run on your main A records, and have you ever been burned by one during an incident? I'm especially curious whether anyone runs 60 seconds permanently and has seen real costs from it.