Almost every "we had downtime during the migration" story reduces to the same root cause. Not a failed data transfer, not a misconfigured web server — a DNS change made without accounting for how long the old answer was already cached.
DNS caching is simple in principle and full of edges in practice. This article covers what actually governs how long a stale answer lives, which TTLs you control and which you do not, and how to sequence a cutover so that the switch itself is a non-event.
What a TTL actually is
Every DNS record carries a time-to-live in seconds. When a resolver fetches a record, it may serve that answer from cache for up to the TTL before asking again.
The subtlety is that the clock starts when the resolver caches the answer, not when you change it. If a record has a TTL of 86400 and a resolver fetched it four hours ago, that resolver will keep serving the old value for another twenty hours — no matter what you do at the authoritative server in the meantime.
This is why lowering a TTL immediately before a cutover achieves nothing. The resolvers that matter already hold the old record with the old, long TTL. The reduction only takes effect after the previous TTL has expired everywhere.
The rule that follows is the single most important thing in this article: reduce the TTL at least one old-TTL period before you need it. If your records are at 86400, lower them a full day in advance. If they are at 3600, an hour is enough.
You can watch this happen. Querying a recursive resolver shows the remaining lifetime of its cached copy, counting down:
dig @1.1.1.1 example.com A
Whereas querying the authoritative server directly shows the configured value:
dig +norecurse @ns1.example.com example.com A
When the first number stops being larger than the second, the reduction has taken hold.
The TTLs you do not control
Three of them, and each has caused a surprise.
The parent delegation. Your NS records exist both in your own zone and at the registry for the TLD. The registry copy is what resolvers follow, and its TTL is set by the registry — commonly one to two days — not by you. Changing nameservers is therefore inherently slower than changing an A record.
Resolver caps. Recursive resolvers apply their own ceilings and floors. BIND will not cache a positive answer beyond its max-cache-ttl, and applies a much shorter ceiling to negative answers. Some public resolvers enforce a minimum, refusing to re-query more often than a few seconds regardless of what you publish. A TTL of 1 does not mean a one-second cutover.
Everything downstream of the resolver. Operating systems cache. Browsers maintain their own DNS caches, with lifetimes largely independent of the TTL. Long-lived connections do not re-resolve at all. There will always be a tail of clients still reaching the old address, and the plan has to tolerate it rather than assume it away.
Negative caching is a separate TTL
If a resolver asks for a record that does not exist, the "does not exist" answer is cached too. The lifetime of that negative answer is not governed by any record’s TTL — it comes from the minimum field of the SOA record (RFC 2308), capped by the SOA record’s own TTL.
example.com. 3600 IN SOA ns1.example.com. hostmaster.example.com. (
2026082401 ; serial
7200 ; refresh
3600 ; retry
1209600 ; expire
3600 ) ; minimum — this is the negative cache TTL
This bites when you add a record that someone has already queried for. A user tries mail.example.com before you have created it, gets NXDOMAIN, and their resolver caches that non-existence for the SOA minimum. You then create the record and it appears not to work — for them specifically, for up to that long.
The practical consequence: create records before anyone is told to use them, and keep the SOA minimum modest. A value of 3600 is reasonable; the 86400 that some zone templates still ship with is not.
Which records to lower, and to what
For a hosting migration, the records that change and therefore need lowering are:
- The apex
A(andAAAA) record www, whether it is a CNAME or its own A record- Any other hostname pointing at the old server —
staging,shop,app MXrecords, if mail is moving too
Records that are not changing do not need touching. There is no benefit to lowering the TTL on a TXT record you are not going to edit.
Set them to 300 seconds. Lower is possible but buys little once resolver floors and client-side caching are accounted for, and it increases query load on your authoritative servers for no practical gain.
Raise them back up after the migration has settled — a week is a reasonable interval. Leaving everything at 300 permanently means every resolver in the world re-queries your zone every five minutes, and it removes your resilience: if your authoritative nameservers become unreachable, a longer TTL is what keeps your site resolving while you fix them.
Sequencing a website cutover
The DNS mechanics only work if the rest of the plan is built around the fact that, for a period, traffic is arriving at both servers.
T minus 7 days. Provision the new environment. Copy the site across. Test it properly by pointing your own machine at the new server, without touching public DNS at all:
# /etc/hosts on your workstation
203.0.113.50 example.com www.example.com
This is the most under-used technique in migration work. It lets you exercise the real hostname against the new server — virtual hosts, TLS certificates, redirects, cookie domains, everything — with zero risk to live traffic.
T minus 7 days. Issue the TLS certificate for the new server now, not at cutover. If you are using an ACME client with HTTP-01 validation this is awkward, because validation requires the hostname to already point at the new server. Use DNS-01 validation instead, which proves control via a _acme-challenge TXT record and works regardless of where the A record points.
T minus 48 hours. Lower the TTLs. Verify with dig against a public resolver until the countdown value drops below the old TTL.
T minus 24 hours. Freeze content changes on the old site. Anything published after this point will need re-syncing manually.
Cutover. Take the final data snapshot — files by rsync, database by dump — and load it onto the new server. For a database-backed site this is the only genuinely disruptive step, and putting the old site into maintenance mode for the duration keeps writes from being lost. On a modest database this is minutes, not hours.
rsync -az --delete /var/www/example.com/ new-server:/var/www/example.com/
mysqldump --single-transaction --routines --triggers example_db | \
ssh new-server 'mysql example_db'
--single-transaction takes a consistent snapshot without locking tables for the duration, on InnoDB. --routines and --triggers are not included by default and their absence is a classic post-migration surprise.
Then change the A record.
Cutover plus minutes. Watch the new server’s access log. Traffic should start appearing within the TTL window and rise steadily.
Cutover plus days. Leave the old server running. Do not switch it off, and do not repurpose the IP. Watch its access log too: the trickle of requests still arriving there is your real-world measure of how much of the long tail remains. When it goes quiet for a full day, you are done.
The step people skip
Keep the old server serving the old content, correctly, for the whole tail period. The temptation is to replace it with a redirect or a holding page. Do not — a visitor whose resolver is still caching the old address will get the holding page instead of the site, and will conclude you have broken something.
If the old and new servers are both serving a working copy of the site, then a stale cache entry is invisible to the visitor. That is what "zero downtime" actually means in a DNS context: not that the switch is instant, but that both answers are correct for as long as both are in circulation.
The exception is anything that writes. If the site accepts orders, comments or form submissions, you cannot have two live copies accepting writes into two separate databases. In that case the old server must go read-only at cutover — displaying content, refusing writes with a clear message — rather than staying fully live.
Changing nameservers is a different job
Everything above assumes you are changing records within a zone you already control. Changing which nameservers are authoritative is slower and riskier, because the delegation TTL at the registry is out of your hands.
The safe approach is to decouple the two changes entirely. First, replicate the zone onto the new nameservers exactly as it currently stands, and verify it answers identically:
dig +norecurse @ns1.newprovider.net example.com ANY
Then change the delegation at the registrar, and wait — a full 48 hours, regardless of what any propagation checker tells you. Only once every resolver is reliably reaching the new nameservers do you start editing records.
Doing both at once means that when something is wrong, you cannot tell whether it is the delegation or the record, and different users will be seeing different combinations of the two.
FXRM handles the DNS planning and cutover as part of every migration onto our UK infrastructure. Talk to us about moving →