DNS Propagation Time: Cloudflare's 3 Seconds vs Your TTL
Short answer (checked 2026-11-03): Cloudflare wrote this week that it halved the time from a DNS record change to the moment its build system has written it out, from about 7.7 to about 3.3 seconds at the median, and cut the memory that does the work by 85%. That is good engineering, but it is not the wait you see when you move a site to a new server. Your wait is set by numbers you choose or can at least read: the record TTL, the negative-cache value in the SOA and, if you change name servers, a two-day TTL at the .com registry. We wrote a small script that reads all of them for any zone, ran it on our zone and on Cloudflare's, and turned the result into a cutover plan.
Source. "We halved DNS build delay and cut memory use by 85%" (Cloudflare blog, published 2026-11-02), discussed on Hacker News with 173 points by 2026-11-03. Their figures are quoted as published; the DNS answers below we queried ourselves.
What Cloudflare changed
A change made through the API or the dashboard goes into a database, then Kafka, then a Zone Builder that turns the zone into keys and writes only the difference to Quicksilver, the key-value store that carries it to every Cloudflare data centre. "Build delay" is the first part only: from the change until Zone Builder has finished writing. Distribution is not included. Record changes grew from about 250 a second in 2022 to about 700, and some zones hold more than 20 million records, so rebuilding a whole zone for one record became expensive. A new planner now picks the smallest safe build; in a 24-hour sample 92.95% of builds avoided a full rebuild.
| Metric (Cloudflare, published 2026-11-02) | Before | After |
|---|---|---|
| Median build delay | about 7.7 s | about 3.3 s |
| Largest p99 spike | 14.6 min | 1.21 min |
| Average working-set memory | 49 GiB | 7 GiB |
| Full-zone builds, share of all builds | 11.88% | 7.04% |
On the new, finer histogram the p99 sits mostly between 10 and 14 seconds. For a customer the useful part is the tail: a change that took a quarter of an hour in a bad minute now takes about one.
The layers you actually wait for
A visitor's resolver does not ask Cloudflare, or any other provider, on every request. It keeps the answer for the record's TTL and only then asks again. So the provider's seconds are added to minutes or days that are decided elsewhere:
| Layer | Who sets it | Value we saw or found |
|---|---|---|
| Provider build | the DNS provider | Cloudflare median ~3.3 s, p99 10-14 s |
| Record TTL | you | 300 s www.cloudflare.com, 1800 s dcxv.com |
| Negative cache (name did not exist) | SOA minimum, RFC 2308 | 300 s cloudflare.com, 3600 s dcxv.com |
| Name-server delegation | the .com registry | 172800 s (2 days) for both zones |
| Serve-stale during an outage | the resolver, RFC 8767 | 1-3 days suggested, sent with TTL 30 s |
The TTL rows come from our run below; the serve-stale row is the RFC's suggestion, not a measurement. Cloudflare's docs say a proxied record is fixed at Auto = 300 s, and a DNS-only record can be set from 60 s (30 s on Enterprise) to 1 day (Cloudflare TTL docs, checked 2026-11-03).
Check a zone yourself: dnscut.js
The script asks the TLD server for the delegation TTL, asks every authoritative server for its SOA serial and the record, and asks three public resolvers what they hold now and how long they will keep it. It needs only Bun's built-in node:dns and dig for the one query without recursion. Save it as dnscut.js:
// dnscut.js - how long until a DNS change is visible everywhere
// usage: bun dnscut.js <zone> [name] e.g. bun dnscut.js example.com www.example.com
import { Resolver } from 'node:dns/promises'
const zone = process.argv[2] ?? 'example.com'
const name = process.argv[3] ?? zone
const PUBLIC = { Cloudflare: '1.1.1.1', Google: '8.8.8.8', Quad9: '9.9.9.9' }
const sys = new Resolver()
const at = ip => {
const r = new Resolver({ timeout: 3000, tries: 2 })
r.setServers([ip])
return r
}
// 1. parent (TLD) delegation: how long resolvers may keep the old name servers
const tld = zone.split('.').pop()
const tldNs = (await sys.resolveNs(tld)).sort()[0]
const tldIp = (await sys.resolve4(tldNs))[0]
const dig = Bun.spawnSync(['dig', '+norec', '+noall', '+authority', 'NS', zone, '@' + tldIp])
const deleg = dig.stdout.toString().split('\n').find(l => /\sNS\s/.test(l))
console.log(`Zone ${zone}, name ${name}`)
console.log(`Delegation at .${tld} (${tldNs}): NS TTL ${deleg?.split(/\s+/)[1] ?? '?'} s`)
// 2. every authoritative server: same SOA serial, same answer?
const serials = new Set()
let ttl = 0
let neg = 0
for (const n of (await sys.resolveNs(zone)).sort()) {
try {
const r = at((await sys.resolve4(n))[0])
const soa = await r.resolveSoa(zone)
const a = await r.resolve4(name, { ttl: true })
serials.add(soa.serial)
ttl = Math.max(ttl, ...a.map(x => x.ttl))
neg = soa.minttl // RFC 2308: negative answers are cached for min(SOA TTL, SOA MINIMUM)
console.log(` ${n.padEnd(24)} serial ${soa.serial} A ${a.map(x => x.address).join(',')} TTL ${a[0].ttl}`)
} catch (e) {
console.log(` ${n.padEnd(24)} ERROR ${e.code}`)
}
}
console.log(serials.size === 1 ? 'Authoritative servers agree.' : `Authoritative servers DISAGREE: ${[...serials].join(' / ')}`)
// 3. public resolvers: what they hold now and how long they may keep it
let left = 0
for (const [label, ip] of Object.entries(PUBLIC)) {
try {
const a = await at(ip).resolve4(name, { ttl: true })
left = Math.max(left, ...a.map(x => x.ttl))
console.log(` ${label.padEnd(10)} ${a.map(x => x.address).join(',')} TTL left ${a[0].ttl} s`)
} catch (e) {
console.log(` ${label.padEnd(10)} ERROR ${e.code}`)
}
}
console.log(`Record TTL ${ttl} s: after a change, caches may serve the old answer for up to ${ttl} s (these three: ${left} s).`)
console.log(`SOA minimum ${neg} s: a resolver that saw NXDOMAIN for a new name may keep it that long.`)
bun dnscut.js dcxv.com
bun dnscut.js cloudflare.com www.cloudflare.com
Output, run on a DCXV Cloud server with Bun 1.4.4 and dig 9.20.24 on 2026-11-03 (our zone first, then Cloudflare's):
Zone dcxv.com, name dcxv.com
Delegation at .com (a.gtld-servers.net): NS TTL 172800 s
ns1.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
ns2.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
ns3.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
ns4.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
ns5.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
ns6.dcxv.com serial 2025120630 A 217.28.140.182 TTL 1800
Authoritative servers agree.
Cloudflare 217.28.140.182 TTL left 1800 s
Google 217.28.140.182 TTL left 1800 s
Quad9 217.28.140.182 TTL left 1800 s
Record TTL 1800 s: after a change, caches may serve the old answer for up to 1800 s (these three: 1800 s).
SOA minimum 3600 s: a resolver that saw NXDOMAIN for a new name may keep it that long.
Zone cloudflare.com, name www.cloudflare.com
Delegation at .com (a.gtld-servers.net): NS TTL 172800 s
ns3.cloudflare.com serial 2418847198 A 104.16.124.96,104.16.123.96 TTL 300
ns4.cloudflare.com serial 2418847198 A 104.16.123.96,104.16.124.96 TTL 300
ns5.cloudflare.com serial 2418847198 A 104.16.124.96,104.16.123.96 TTL 300
ns6.cloudflare.com serial 2418847198 A 104.16.124.96,104.16.123.96 TTL 300
ns7.cloudflare.com serial 2418847198 A 104.16.124.96,104.16.123.96 TTL 300
Authoritative servers agree.
Cloudflare 104.16.124.96,104.16.123.96 TTL left 242 s
Google 104.16.124.96,104.16.123.96 TTL left 300 s
Quad9 104.16.123.96,104.16.124.96 TTL left 181 s
Record TTL 300 s: after a change, caches may serve the old answer for up to 300 s (these three: 300 s).
SOA minimum 300 s: a resolver that saw NXDOMAIN for a new name may keep it that long.
Four things the output says
- Equal serials mean the provider is done. The same SOA serial on every name server means the change has left the provider; the rest is caches. Anycast servers can differ by location, so this holds for the place you query from. If serials differ, wait or open a ticket: no TTL trick helps.
- Resolvers count down, they do not refresh early. Quad9 had 181 s left on www.cloudflare.com and Cloudflare's resolver 242 s. A change made at that moment would have reached their users three to four minutes later, however fast the build. On dcxv.com the same wait can be 30 minutes, because our A record has a TTL of 1800 s.
- A new name can be slower than a changed one. If someone looked a name up before it existed, their resolver may keep the "no such name" answer up to the SOA minimum: 3600 s on our zone. Create new host names before you announce them.
- Changing name servers is a different scale. The .com delegation carries 172800 s, two days. Moving DNS to another provider means both providers serve identical zones for at least that long.
A cutover plan for a new server or a new IPv4 block
- One old TTL before: lower the A and AAAA records to 300 s. The full checklist is in how to migrate to a cloud server.
- At the switch: change the records, run
dnscut.jsuntil the serials agree, then watch the resolver TTLs count down to the new address. Keep the old server answering until its logs go quiet. - If name servers change: build the new zone first, switch the NS at the registrar, and keep the old provider serving the same records for at least two days after.
- New addresses: if the move also brings a freshly bought or leased block, check reverse DNS and geolocation before traffic arrives; see fixing IPv4 geolocation after a transfer and our IPv4 brokerage.
- After a week: raise the TTL again (1800-3600 s is common), so a short provider outage does not empty every cache within minutes.
Plan the move with us
Our Tier III data centres in Prague and Covilha run cloud servers and dedicated servers. Start a cloud server, run dnscut.js on your zone the day before the move and send the output to sales@dcxv.com: we will help you plan the switch.
