Lower Your DNS TTL Before You Migrate a Server (A Checklist)

A 24-hour TTL turns a server move into a day of half-broken DNS. When to lower it, how to check what resolvers see with dig, and what to tell DNS support.

Share
Lower Your DNS TTL Before You Migrate a Server (A Checklist)

Every record in a zone I manage had a TTL of 86400 seconds. That's 24 hours. Nobody chose it; it was the provider's default and nobody had touched it in years. It doesn't matter at all — until you move a site to a new server. Then it means: for up to a day after you change the A record, some part of the internet still goes to the old IP.

TL;DR: At least one full old TTL before the move, lower the TTL of every record you'll touch to 300. Wait. Then change the records. Check the authoritative nameservers and public resolvers with dig. Keep the old server running until the old TTL has definitely expired. Afterwards raise the TTL back to something like 3600.

# what the authoritative server says (ground truth)
dig +noall +answer example.com A @ns1.example-dns.net

# what a public resolver currently has cached (TTL counts down)
dig +noall +answer example.com A @1.1.1.1
dig +noall +answer example.com A @8.8.8.8

What actually went wrong

Two things, from real moves.

The stale resolver. A site moved to a new server, the record was updated, the zone was correct, I'd verified it. Someone on the client's side, travelling, got a timeout. Their resolver still had the old IP cached — and the old server was already gone. From my side everything checked out, from theirs the site was down. Nothing to fix except wait out the 24 hours, which is a terrible thing to tell a client.

The record that got added instead of replaced. The zone sits with a DNS provider where changes go through their panel or support. In one move the A record was supposed to be switched to the new server. What happened on the provider side: the new A record was added, the old one stayed. Two A records for the same name means round robin — roughly half the clients hit the old server, half the new. Some requests worked, some didn't, depending on which answer a resolver handed out. It also broke Let's Encrypt validation; that's its own post: Let's Encrypt fails with a duplicate A record.

A low TTL wouldn't have prevented the second mistake, but it would have made it cheap to fix. With 86400 everywhere, every mistake costs a day.

How TTL actually works

A resolver that looks up your record caches it for the TTL that came with the answer. Until that runs out it doesn't ask again. So:

  • Changing the record doesn't affect anyone who already has it cached.
  • Changing the TTL also doesn't affect anyone who already has it cached — they keep the old TTL until it expires.

That second point is the one people miss. Lowering the TTL from 86400 to 300 and changing the IP five minutes later does nothing useful. Resolvers that cached the record an hour ago still hold it for another 23 hours. You have to lower the TTL first, then wait at least one old TTL (24 hours here), and only then is everyone on the short TTL.

The checklist

1. Inventory what you'll touch

Not just the website A record. Everything that points at the old server:

for r in A AAAA MX TXT CNAME; do
  dig +noall +answer example.com $r @ns1.example-dns.net
done
dig +noall +answer www.example.com @ns1.example-dns.net
dig +noall +answer mail.example.com @ns1.example-dns.net

Find the authoritative nameservers first if you don't know them:

dig +short NS example.com

Don't forget mail. If mail.example.com or the MX target moves too, lower those records' TTL as well. Mail is more forgiving (sending servers retry), but mail clients configured with mail.example.com will happily keep connecting to a dead box. And watch out for wildcards — a *.example.com record silently covers every subdomain you didn't list.

2. Lower the TTL to 300 — at least one old TTL in advance

For a 24-hour TTL: do it at least a day before, two if you want margin. 300 seconds is a sane value for the migration window. Lower isn't really better; some resolvers enforce their own minimum anyway.

Then verify the authoritative servers actually serve the new TTL:

dig +noall +answer example.com A @ns1.example-dns.net
# example.com.  300  IN  A  203.0.113.10

The second column is the TTL. If it still says 86400 there, the change didn't land — no point waiting.

3. Prepare the new server completely

Test it before DNS points at it. curl --resolve sends requests to the new IP with the right hostname, no DNS change needed:

curl -sI --resolve example.com:443:198.51.100.20 https://example.com/

Or a temporary hosts-file entry on your own machine. Note: a Let's Encrypt certificate via HTTP-01 can only be issued once the name points at the new server, so plan for a short window or use DNS-01.

4. Cut over

Change the records. Then check, in this order:

# authoritative: is the change live, and is it the ONLY answer?
dig +noall +answer example.com A @ns1.example-dns.net

# public resolvers: when does it show up, what TTL is left?
dig +noall +answer example.com A @1.1.1.1
dig +noall +answer example.com A @8.8.8.8

Look closely at the authoritative answer. One A record with the new IP. If you see two lines, you've got the round-robin problem from above — fix it right now, not later.

5. Keep the old server alive

Don't shut it down at cutover. Leave it running at least one old TTL after the change — the TTL that resolvers might still have cached, not the new one. If you followed step 2 properly, that's 300 seconds; if somebody skipped it, it's a day. Watch the old server's access log: when real traffic stops arriving there, you're done.

tail -f /var/log/apache2/access.log     # or nginx, whatever served it

For mail, the old box should keep accepting (or at least not bounce) until the MX change has propagated.

6. Raise the TTL again

After everything is stable, set the TTL back up. I go with 3600 — an hour is short enough that the next move only needs an hour of lead time, and long enough that you're not hammering the nameservers. Leaving it at 300 forever isn't a disaster, but you lose caching for no reason.

When someone says "it doesn't work for me"

Debug order:

  1. Authoritative nameservers — is the zone correct and single-valued?
  2. Public resolvers — does 1.1.1.1 / 8.8.8.8 have the new value, and what TTL is left?
  3. The target server's access log — does the user's request even arrive?

If the zone is right and nothing arrives at the new server, it's a cache on their side — resolver, router, OS. On their machine nslookup example.com shows what they get. Flushing the local cache sometimes helps; a stale ISP resolver you just have to wait out.

Ready-made text for DNS support

If your DNS changes go through a support ticket, be painfully explicit. "Please point example.com to the new server" is how I ended up with two A records.

Hello,

for the zone example.com, please make the following changes:

  1. Today: set the TTL of the records example.com A, www.example.com A and mail.example.com A to 300 seconds. Do not change the values yet.
  2. On [date, at least 24 h later]: replace the existing A record example.com (currently 203.0.113.10) with 198.51.100.20. The old value must be removed — there must be exactly one A record for this name afterwards. Same for www and mail.
  3. After [date + a few days]: set the TTL of these records back to 3600.

Please don't change any other records (MX, TXT/SPF, DKIM).

Thanks!

The words "replace", "remove" and "exactly one" are doing a lot of work there. Use them.

Make it permanent

Honestly, the best fix is boring: set the default TTL on your zones to something like 3600 now, while nothing is moving. Then the next migration needs an hour of lead time instead of a day, and you'll never have to tell a client "wait until tomorrow".

If you're in the middle of a larger move — like the one from Plesk to KeyHelp that kicked this off — put the TTL step at the very top of the plan, before any data gets copied. And if a provider outage forces an unplanned move, the TTL is already too late; that's one more reason to keep it short by default, see what a provider outage taught me about backups.


Related posts