Skip to content
Networking

Change a DNS record without a long stale window

A DNS change takes effect when several independent caches expire, and the TTL you set is only one of them. Lowering it at the moment of the change barely helps. Lowering it one full old TTL ahead does.

  • Practitioner
  • 3 min read
On this page

Before you start

  • Access to change records at your DNS provider.
  • dig on your machine, or any tool that can query a chosen resolver.
  • The old backend can keep serving for the length of the change.

Where the old answer lives

A resolver that cached the old record keeps serving it until its TTL runs out, and nothing you do at the authoritative server shortens that remainder. A name that did not exist when someone looked it up stays missing for the negative TTL. And client runtimes keep their own caches on top of the resolver: some honour the TTL, some do not.

The change plan

  1. Step 1Write down the current TTL

    The second column of the answer is the TTL in seconds as this resolver sees it. Ask the authoritative server directly to read the configured value instead of a remainder.

    bash
    dig +noall +answer app.example.com A
    dig +noall +answer app.example.com A @ns1.example.com
  2. Step 2Read the negative TTL

    Per RFC 2308, a resolver caches a missing name for the smaller of the SOA record's own TTL and its last field. If the change creates a new name, create it before anything looks it up.

    bash
    dig +noall +answer example.com SOA
  3. Step 3Lower the TTL one old TTL ahead

    Drop the TTL to something short, such as 60 seconds, then wait at least one full old TTL before the change. A cache that fetched the record before you lowered it holds it for the old TTL, so the new value only helps caches that refresh after the wait.

  4. Step 4Change the record, keep the old backend

    Point the record at the new address and leave the old backend running. Some clients will keep reaching it until every cache in front of them has expired.

  5. Step 5Check from several resolvers

    One resolver is one opinion. Compare the authoritative answer with at least three public resolvers, and decide in advance which answer means done.

    bash
    for r in ns1.example.com 1.1.1.1 8.8.8.8 9.9.9.9; do
      echo "$r: $(dig +short app.example.com A @"$r")"
    done
  6. Step 6Wait for the slowest client

    The window after the change is the new TTL plus the longest client cache. Long lived processes and JVM services are the usual laggards: the JVM keeps its own cache controlled by the networkaddress.cache.ttl security property, and -1 caches forever. Check each client in staging and plan for the slowest one.

  7. Step 7Raise the TTL, then retire the old backend

    When the resolvers agree and the slowest client has moved, restore the long TTL. Retire the old backend last, after one more old TTL has passed.

A starting TTL policy

  • General records such as web, API and mail hosts: an hour or more. RFC 9199 advises long TTLs for resilience and latency.
  • NS and MX records: an hour to a day. A delegation change waits out the larger of the parent and child NS TTLs, so keep the old servers for that long.
  • Health checked failover records: short, a minute or two, and only useful once the health check has fired.
  • TTL 0: avoid it. It turns off caching entirely and moves the load onto your authoritative servers.

What to watch

  • The authoritative answer and the answers from several resolvers, compared.
  • The remaining TTL on a key record over time: a value that stays high after a change means a cache still holds the old copy.
  • The NXDOMAIN rate for names that should exist: a spike after a deploy is the negative cache at work.
  • SERVFAIL rate plus a probe straight to the authoritative server, because a resolver serving stale answers can stay green while the authoritative is down.
Keep going
Next

Need a hand with your own stack?

We build and run this kind of infrastructure for our own products and for clients.