DNS TTL Explained: How to Set It Before a Migration

A DNS record's TTL (time to live) is the number of seconds a resolver may keep a cached copy before asking again. It's the single setting that decides whether a DNS change takes five minutes or a day. I've covered why changes seem slow in DNS propagation explained. This post is the practical companion: how to see the TTL that's really in effect, which values to use, and exactly when to change them around a migration.

Reading the TTL that's actually in force

The TTL is the second column in dig output:

dig example.com A
example.com.    1800    IN    A    192.0.2.10

There's a catch. If you ask your normal resolver, that number is the time remaining in its cache, and it counts down each time you repeat the query. To see the value you actually configured, ask one of the domain's authoritative name servers directly:

dig NS example.com +short
dig @ns1.example.net example.com A +norecurse

The authoritative server always reports the full configured TTL. If the two numbers differ, the first is simply a cache partway through its countdown.

The TTLs you control, and sensible values

There's no official "correct" TTL. It's a trade: a long TTL means fewer queries and more resilience if your DNS provider has a blip, and a short one means faster changes. Some reasonable starting points:

  • A, AAAA and CNAME for a website: 3600 (an hour) is a fine resting value for a site that rarely moves. Use 300 if you change hosts often or use DNS-based failover.
  • MX: mail servers rarely change, so 3600 or more is fine. Mail also retries for days when delivery fails, so a short outage costs less here than on the web.
  • TXT for SPF, DKIM and DMARC: 3600 is typical. Drop it before editing SPF so a mistake can be corrected quickly.
  • Records during a migration: 300. Going much lower brings little benefit and some providers won't let you. Cloudflare's minimum is 60 seconds (30 on Enterprise plans).

If your records are proxied through a CDN, the TTL may not be yours to set. Cloudflare, for example, fixes proxied records at "Auto", which is 300 seconds, because the address visitors see belongs to Cloudflare, not your server.

The TTLs you don't control

Three other timers affect how long changes take, and people forget them.

Name server delegation. The NS records that point a domain at its DNS provider live in the parent zone, run by the registry. For .com, they're served with a TTL of 172800 seconds, which is two days:

dig NS example.com @a.gtld-servers.net +norecurse

Look at the TTL in the authority section. You can't lower it, which is why moving to a new DNS provider has a slow tail that editing a record doesn't.

Negative caching. When a name doesn't exist, resolvers cache that too. RFC 2308 sets the negative cache time as the smaller of the SOA record's own TTL and the last field of the SOA record (historically called "minimum"). Check yours:

dig SOA example.com +short
ns1.example.net. hostmaster.example.com. 2026100401 7200 3600 1209600 3600

The final 3600 means a lookup for a missing name is remembered for up to an hour. RFC 2308 suggests one to three hours as a sensible default. That's why creating a record after someone has already looked it up can seem slow.

Resolver limits. RFC 2181 says the TTL is a maximum, not a promise, and resolvers may cap it. BIND caps cached answers at one week by default and negative answers at three hours. Unbound caps at one day by default. So a TTL of 604800 or more often gets shortened anyway. A few resolvers also keep serving stale records if your authoritative servers go unreachable (RFC 8767 describes this), which is another reason not to shut old infrastructure down too early.

A migration timeline that works

The key fact: the TTL that matters on the day of the change is the one resolvers cached before it. Lowering the TTL and changing the record in one sitting achieves almost nothing. Here's the schedule I use, assuming the current TTL is 86400 (one day):

  1. Two days before (T-48h): lower the TTL on every record you'll change to 300. Include both A and AAAA if you have IPv6, and www as well as the bare domain.
  2. Wait at least one full old TTL. With 86400 that's a day. Every cache now holds the short TTL. Confirm with the authoritative query above.
  3. Change day (T-0): update the records. Resolvers pick up the new value within about five minutes.
  4. Watch it land. Check a spread of resolvers with our DNS propagation checker rather than refreshing your own browser, which sits behind two caches on your machine.
  5. Keep the old server running for at least one old TTL, preferably longer. Some resolvers ignore short TTLs and some clients cache connections for a long time.
  6. A day or two later: raise the TTL back to its resting value.

If the old TTL was 3600, the same plan compresses to a few hours. If you don't know the old TTL, look it up now, before you touch anything.

For moves where DNS is only one part of the job, see the full checklist in how to move a website without downtime.

Common mistakes

  • Forgetting AAAA. You move the A record and leave the AAAA pointing at the old host. IPv6 visitors keep hitting the old server, and it looks like propagation is "stuck" for some people.
  • Lowering the wrong record. If www is a CNAME to the bare domain, the TTL on both records counts.
  • Leaving TTL at 60 forever. It works, but every visitor's resolver has to ask again every minute, and a short outage at your DNS provider hits users almost immediately instead of being absorbed by caches.
  • Changing name servers and records at once. Do one, let it settle, then the other. Two moving parts at once make problems very hard to diagnose.

Frequently Asked Questions

What's the lowest TTL I should use?

For a planned change, 300 is plenty. Lower values mainly add query load. Some providers set a floor anyway, commonly 60 or 30 seconds.

Does lowering the TTL slow down my website?

Slightly more DNS lookups happen, and a visitor whose cached entry has expired waits one extra round trip to the resolver. For most sites it isn't noticeable, but there's also no benefit to keeping a short TTL when nothing's changing.

Why does dig show a different TTL each time I run it?

You're querying a caching resolver, which reports the time left before its copy expires. Query the authoritative name server with +norecurse to see the configured value.

Can I set a TTL of zero?

It's allowed by the standard and means "don't cache". In practice many resolvers apply a minimum anyway, and many DNS providers won't accept zero. Don't rely on it.

Was this useful?
Share

Related reading

0 comments

No comments yet. Be the first.

Leave a comment

Comments are reviewed before they appear. Your email is optional, is never published, and is only used if we need to reply.

« Back to Blog