Lower the TTL before the change, not after
The record is changed. Half the world sees the new server, half still sees the old one. Somebody lowers the TTL to five minutes and everybody waits five minutes.
Nothing happens, because a resolver holding a cached answer does not come back for the new TTL. It comes back when the old one expires, and the old one is the value that was in force at the moment it last asked. Lowering the TTL now affects the next change, not this one.
The order that works
Three steps, and the waiting is in the middle.
Lower the TTL. To 300 seconds, on the records you are about to move.
Wait the old TTL. The full value, not an estimate. If it was 3600, wait an hour. If it was 86400, wait a day. After that, every resolver that had a cached copy has expired it and re-fetched, and what it re-fetched carries the new short TTL.
Change the record. Now the maximum staleness anywhere is five minutes.
Which means the number that decides your migration window is the TTL as it was two days ago, and it is the one thing about a cutover you cannot shorten on the day. Read it before you plan anything:
dig +noall +answer example.com A
The second column of that output is the remaining lifetime of the cached copy at whichever resolver answered, counting down. Ask the authoritative server instead to see the configured value rather than a countdown:
dig @ns1.provider.net +noall +answer example.com A
A timetable for a cutover
| When | Do |
|---|---|
| Before anything | Record the current TTL on every record you will touch, and on the SOA |
| Old TTL plus a margin, before the cutover | Lower those records to 300 |
| The day before | Verify the short TTL is being served by several public resolvers, not just yours |
| Cutover | Change the records. Both servers stay up. |
| After | Watch the old server's access log, not the clock |
| When the old log goes quiet, plus a day | Raise the TTL back and only then decommission |
The fifth row is the one that turns this from a ritual into a measurement. Traffic still arriving at the old machine is the definitive statement that somebody is still being sent there, and no amount of checking from your own resolver will tell you that. Keep the old server answering, correctly, for as long as it is receiving requests. Turning it off on a schedule while it is still serving real visitors is the actual failure people are trying to avoid.
The TTLs that are not on the record you changed
Three others govern parts of this, and two of them are not yours.
The delegation. The NS records that point at your nameservers exist twice: in your zone, where you set the TTL, and at the parent registry, where you do not. The registry publishes them with its own value, commonly a couple of days for generic top-level domains. This is why changing which company hosts your DNS is a slower operation than changing a record inside it, and it has to be planned differently: the thing expiring is a cache, and this cache has a long lease you cannot shorten.
Negative caching. When a resolver is told a name does not exist, it remembers that too, for a period taken from the zone's SOA record rather than from any individual record. So a newly created hostname can take longer to become visible than its own TTL suggests, if anything looked it up before it existed. On a launch day this is the reason a new subdomain appears for some people and not others, and the fix is to check the SOA value in advance rather than to keep re-creating the record.
dig +noall +answer example.com SOA
The last field of that answer is the negative caching value.
Whatever the resolver decides. Resolvers are permitted to clamp. Some enforce a floor, refusing to re-query more often than a set interval regardless of what you published. Some cap the maximum. Your 300 is a request, not an instruction.
The caches below DNS
Even a resolver behaving perfectly is not the last hop. Between it and the user sit three more layers with their own timings.
The operating system keeps a cache. Browsers keep their own, separate from it, which is why a page can keep reaching the old address after the machine's cache has been flushed. And application runtimes cache resolution too, in some cases for the lifetime of the process unless explicitly configured otherwise, which is how a long-running service keeps talking to a decommissioned database for days after the record moved.
That last one is the one to check before a backend migration. Restarting the process is frequently the only way to make it look again, and "restart everything that talks to this hostname" belongs on the cutover checklist rather than being discovered at midnight.
What a low TTL costs, and when to put it back
Five minutes everywhere, permanently, is a defensible choice and it is not free.
Every expiry is a query, so a short TTL multiplies traffic to your nameservers, and it makes their availability matter far more: with a day-long TTL, an authoritative outage is invisible to most users for most of a day, while at five minutes it becomes an outage almost immediately. A short TTL also adds a resolution round trip to a share of visits, which is real if small.
The sensible resting position for a stable site is an hour or a few hours, with the pre-migration ritual above whenever something moves. Two exceptions are worth keeping short permanently: any record used for failover, where the whole point is speed, and any record pointing at infrastructure whose address is not under your control, because that address can change without warning and you want the correction to take effect quickly: the address belongs to somebody else.