Lowering DNS TTL Before a VPS Cutover: A Measured Rollback-Safe Procedure

A cutover fails in the rollback, not in the switch. The switch is one edit to an A record; the rollback is where you discover a resolver on the other side of the world cached your old address for another 23 hours. Every plan that says “we can always roll back” is really a statement about TTLs, and TTLs are the one part of DNS that behaves differently in every resolver.

This procedure lowers TTLs ahead of a VPS move, verifies the lower TTL has actually propagated before the switch, and defines a rollback window that is measured rather than assumed. It assumes the source and destination are different VPS instances you control, which is the only case where the old address can be made to answer again on demand.

Every number below comes from a four-location probe setup — Frankfurt, Ashburn, Singapore, and São Paulo — resolving an authoritative record we control. The resolver mix matters because TTL behaviour is a property of the recursive layer, not of your zone file.

Why lowering a TTL is not instantaneous

When you publish a TTL of 60, you are telling recursive resolvers how long they may cache the record from the moment they fetched it. It does not reach back into caches that already hold the old record. A record with a 24-hour TTL fetched at 09:00 is still served from cache at 20:00, even if you changed it to 60 seconds at 09:05.

The consequence: the wait period is the old TTL, not the new one. This is the most common mistake in cutover planning. If your current TTL is 86,400 seconds, lower it and then wait at least 86,400 seconds before switching, plus resolver margin.

The second layer is resolver behaviour. Some resolvers enforce a minimum cache time below which they will not go — some ISP resolvers clamp to 300 seconds, and a few public resolvers have historically clamped as high as 3,600. Your 60-second TTL may be honoured as 300. Plan for the clamp, and measure it rather than trusting the vendor documentation.

Set the target TTL and confirm the clamp

Four hours before you begin, publish the record with the final low TTL. We use 60 seconds for cutovers with a short blast radius and 300 seconds when the host is behind a CDN that adds its own TTL. Query several public resolvers directly and inspect both the answer and the TTL it reports:

for r in 1.1.1.1 8.8.8.8 9.9.9.9 208.67.222.222; do
  printf "%-16s " $r
  dig +noall +answer A example.com @$r | awk '{print $2, $5}'
done

# authoritative view (bypasses every cache)
dig +noall +answer A example.com @ns1.example.net

The number in the response is the remaining TTL, so a fresh fetch shows your new value and a later fetch shows it decaying. A resolver whose TTL never drops below 300 is clamping, and your effective cutover window is 300 seconds, not 60.

On our probe set, 11 of 14 resolvers honoured a 60-second TTL exactly. Two clamped to 300, and one — a national ISP resolver in São Paulo — held the record for 1,800 seconds regardless of the TTL we published. That single resolver set the effective rollback window for the whole migration.

Measure propagation from where users actually are

A local dig proves nothing. Query vantage points that match your user distribution and compare answers to the expected new value. If you control the zone, log every authoritative query during the window to see which resolvers are still asking.

# quick multi-vantage check (one entry per resolver)
for ns in 1.1.1.1 8.8.8.8 9.9.9.9; do
  echo "== $ns"
  dig +short A example.com @$ns
done

# authoritative query log: who is still resolving during the window
tcpdump -ni eth0 -c 200 'udp port 53' -l | \
  awk '{print $3}' | sed 's/\.[0-9]*:$//' | sort | uniq -c | sort -rn | head

# per-resolver TTL distribution over 10 minutes
for i in $(seq 1 10); do
  dig +noall +answer A example.com @1.1.1.1 | awk '{print $2}'
  sleep 60
done | sort -n | uniq -c

Record the results before you move anything. This is your evidence for whether it is safe to proceed, and the artifact you want when someone asks why the rollback took 40 minutes instead of five.

A measured propagation table

Results from lowering a production A record from TTL 3600 to 60 and waiting 3,700 seconds (one hour plus 100 seconds of margin). “Old-address answers” is the count of probes still receiving the previous IP after the switch.

Vantage Resolver Published TTL Observed TTL Observed decay Old answers after switch
Frankfurt 1.1.1.1 3600 s 60 s 42 s 0
Ashburn 8.8.8.8 3600 s 60 s 95 s 0
Singapore 9.9.9.9 3600 s 60 s 128 s 2
São Paulo ISP resolver 3600 s 1800 s 1,847 s 11
Regional ISP local resolver 3600 s 300 s 312 s 4

Median time to full propagation across the public resolvers was 88 seconds. The two outliers — the ISP resolver clamping to 1,800 and the regional resolver at 300 — account for 15 of the 17 stale answers observed eight minutes after the switch. If your traffic is concentrated in a region with a clamping resolver, the cutover is effectively that resolver’s clamp duration, and no TTL you publish will change it.

Cut over with the old host still answering

Rollback safety does not come from DNS. It comes from keeping the old host alive, with its data in sync, during the switch. Do not destroy or shut down the source instance when you change the record: leave it running on the old IP for one full old-TTL period plus the observed worst-case propagation.

If the application writes to a database, keep replication from source to destination live and delay the cutover until replication lag is under a second. Then:

# before the switch: confirm sync one more time
mysql -e "SHOW REPLICA STATUS\G" | grep -E "Seconds_Behind|Running"

# switch
# (provider API or registrar) example.com A -> 203.0.113.20

# immediately after: verify from multiple resolvers
for ns in 1.1.1.1 8.8.8.8 9.9.9.9; do dig +short A example.com @$ns; done

# watch the new host's access log for traffic arriving
journalctl -u nginx -f -o cat | awk '{print $1}'

The rollback decision point is a threshold, not a feeling. Ours: if more than 10% of requests still hit the old host 30 minutes after the switch and the old host is at risk (expiring reservation, diverging data, failing disk), roll back. Define that before the switch and record who is authorised to call it.

Rollback: revert the record, keep the TTL low

Rolling back is the same operation in reverse, and it is fast only because the TTL is still low. Revert the A record, keep the low TTL for at least one full old-TTL period after stability returns, and only then raise it back. Raising the TTL immediately after a rollback caches your outage for a day.

Two things get forgotten in a rollback. The destination may have accepted writes since the switch — reconcile or deliberately discard them before the source becomes authoritative again. And TLS certificates or application state on the source may have drifted; verify it still serves a valid chain before reverting:

echo | openssl s_client -connect 198.51.100.10:443 -servername example.com \
  2>/dev/null | openssl x509 -noout -dates -subject
curl -sS -o /dev/null -w "%{http_code} %{time_total}\n" -H "Host: example.com" \
  --resolve example.com:443:198.51.100.10 https://example.com/

Only after the revert has propagated and the error rate is back to baseline do you raise the TTL. Raise it in stages — 300, then 3,600, then 86,400 — verifying at each step rather than jumping straight back to a day.

Checklist with timings

The full sequence as an offset from the switch. Every wait derives from the old TTL, not the new one.

Offset Action Verification
T-26h Record baseline: old TTL, resolver clamp survey clamp max 1,800 s
T-26h Publish TTL 60 on unchanged IP resolver TTL distribution
T-2h Re-verify propagation; replication lag < 1 s lag metric
T-0 Change A record; keep source running error rate, log share
T+30m Threshold decision: proceed or roll back >10% stale = rollback
T+25h Raise TTL to 300, then 3600, then 86400 errors flat
T+48h Destroy source instance snapshot taken

The 25-hour figure is the old 3,600-second TTL plus the 1,800-second clamp with a generous margin; on a zone that was already at 300 seconds the entire schedule compresses to about an hour. This is why the baseline survey at T-26h matters more than any other step: it tells you which of those two schedules you are actually running.

Do the survey, publish early, verify from real vantage points, keep the source answering until at least one old-TTL period has passed, and define the rollback threshold in numbers before the switch. If you are staging this migration as a move from shared hosting to a VPS where you will not control the old host, note that the rollback option disappears — the old platform cannot be kept answering, so the TTL window becomes the only safety margin you have.

Leave a Reply