A company changes DNS providers on a Friday. The new zone is byte-identical to the old one, checked record by record with dig @ns1.newprovider.net; the nameservers are changed at the registrar; the old provider is left running. Over the weekend the domain is dead for some customers and fine for others, monitoring is green, and both sets of nameservers answer correctly. The parent still publishes a DS record fingerprinting the old provider’s key, on a TTL of a day that nobody at either provider can change, and everyone behind a validating resolver gets SERVFAIL until it expires.
A zone has two publishers, you and your parent, and almost everything written about DNS assumes there is one. The parent publishes two facts about your zone, who serves it and which key may sign it, and nothing anywhere compares either with your zone. Six clocks decide when a change is finished; two of them are yours, and neither governs a nameserver change or a DNSSEC change. Every number here came off one machine: Ubuntu 24.04.4 with BIND 9.18.39, Knot DNS 3.3.4 and Unbound 1.19.2, running a signed root, TLD and zone on loopback with a primary, two secondaries and two validating resolvers. Those are the LTS versions, not upstream’s.
The six stages, and the one query that reads the parent.
- What the parent publishes about you: the delegation, the glue and the DS, none of which you can write.
- The zone, the serial and the copies: why a serial that goes backwards cannot be undone.
- Which copy answers: three servers, all authoritative, two different answers, no error anywhere.
- The chain the parent vouches for: a hash of a key, held by a stranger, never checked against your zone.
- The clocks: six of them, and four belong to somebody else.
- When the change is over: the only question you have, and the one with no global answer.
Ask a nameserver for the level above you, recursion off, DNSSEC on, and read the authority and additional sections:
DOMAIN=example.com
PARENT=$(dig +short NS "${DOMAIN#*.}" | head -1)
dig +norec +dnssec @"$PARENT" "$DOMAIN" A +noall +authority +additional- An NS set that is not the one in your zone. The parent’s list is the one used; yours is decorative. Stage 1.
- An NS name inside your zone with no address beside it. Missing glue. Stage 1.
- A named server does not answer, or answers without
aa. It is in the rotation and not serving your zone. Stage 3. - Every named server answers with
aa, and the SOA serials differ. The copies have diverged. Stage 2. - A DS here, and a validating resolver says SERVFAIL. The parent vouches for a key your zone no longer has. Stage 4, the branch that takes domains off the internet.
- No DS at all, an NSEC or NSEC3 in its place, and you believe DNSSEC is on. Your signatures are ignored by everybody. Stage 4.
- All of that correct, and a resolver still gives the old answer. Nothing is broken; a clock has not run out. Stages 5 and 6.
Two limits. This response is itself cached, at the parent’s TTL, so a resolver that asked before your change still holds the previous version; that is stage 6. And if your domain sits inside somebody else’s delegated subtree, your parent is not a registry, you have no channel to it, and most of stages 1 and 4 are unavailable to you. The client’s side of the wire is DNS, All the Way Down; buying the domain and choosing its records is Getting a Domain and Its DNS Right.
Stage 1: What the parent publishes about you
A registry holds three things about your domain, and you can write none of them directly:
What the registry actually stores.
- The delegation NS set: the names of the servers resolvers will ask.
- Glue: addresses, but only for nameserver names that sit inside the domain being delegated.
- The DS record: a hash of one of your keys, if you use DNSSEC.
Your A records, your MX, your TXT and your TTLs live at your DNS provider; the registry has never seen them. Your registrar’s control panel is a channel to those three things, not a copy of them. There is no read-back path. The only way to see what is published is to ask a parent nameserver.
A real referral from the lab’s TLD server, recursion off, DNSSEC on. This is the parent’s entire opinion of the zone, in one packet:
$ dig +norec +dnssec @127.0.0.11 example.test A +noall +authority +additional
example.test. 3600 IN NS ns1.example.test.
example.test. 3600 IN NS ns2.example.test.
example.test. 3600 IN NS ns3.example.test.
example.test. 3600 IN DS 24922 13 2 BC2049B4...8CA1808C
example.test. 3600 IN RRSIG DS 13 2 3600 20261003215802 20260903215802 48667 test. ...
ns1.example.test. 3600 IN A 127.0.0.21
ns2.example.test. 3600 IN A 127.0.0.22
ns3.example.test. 3600 IN A 127.0.0.23The NS records decide which servers get asked; the addresses under them are glue; the DS decides which key of yours may sign; the RRSIG proves the DS came from the parent. The NS set carries no signature. It is not authoritative data and DNSSEC does not protect it. The child’s copy of the same list is signed, by the child. Two copies in two zones, and nothing in any protocol, implementation or registry check ever compares them. In this lab they disagreed for hours and nothing logged anything.
The command everybody runs shows the wrong list
dig example.com NS does not show the delegation. It asks your recursive resolver, which answers from cache with the child’s apex NS set. Here the parent published ns1, ns2, ns3 and the zone published ns1, ns2, ns4:
$ dig +norec @127.0.0.11 example.test NS +short | sort | tr '\n' ' ' # the parent
ns1.example.test. ns2.example.test. ns3.example.test.
$ dig @127.0.0.30 example.test NS +short | sort | tr '\n' ' ' # Unbound
ns1.example.test. ns2.example.test. ns4.example.test.
$ dig @127.0.0.31 example.test NS +short | sort | tr '\n' ' ' # BIND
ns1.example.test. ns2.example.test. ns4.example.test.In the exact case you would run this command to detect, the two lists disagreeing, it shows you the one nobody uses.
A server the parent does not name receives nothing
This is the belief that makes a spare nameserver look like insurance. multi.test was delegated by the parent to nsa only; the zone’s own NS set named nsa and nsb; nsb was up and answering correctly. Then nsa was stopped:
$ dig +norec @127.0.0.29 who.multi.test +short
203.0.113.29 <- nsb, healthy, authoritative, answering
$ dig @127.0.0.31 who.multi.test | grep status:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 29813
$ dig @127.0.0.30 who.multi.test <- Unbound: no answer in six secondsEvery query the resolver sent, in order, with the healthy server’s address appearing zero times:
IP 127.0.0.1.56128 > 127.0.0.31.53: A? who.multi.test.
IP 127.0.0.1.42382 > 127.0.0.10.53: NS? .
IP 127.0.0.1.54872 > 127.0.0.10.53: NS? test.
IP 127.0.0.1.45427 > 127.0.0.11.53: NS? multi.test.
IP 127.0.0.1.36563 > 127.0.0.28.53: A? who.multi.test. <- the dead one
IP 127.0.0.31.53 > 127.0.0.1.56128: ServFail
$ tcpdump -r cap2.pcap -n | grep -c "127.0.0.29"
0It holds with a warm cache too. Adding a nameserver to your zone does not add a nameserver. It adds a name to a list used for one thing, deciding who gets a NOTIFY (stage 2). The order matters: the new server must hold the zone before the delegation changes. Add it at the registrar first and you have delegated to a server answering REFUSED, which resolvers treat as lame for the whole of the parent’s TTL. Add it to your zone first and it gets NOTIFY but no traffic.
Glue
Glue is needed when a nameserver’s name is inside the zone it serves. There are two neighbouring cases, and one of them is how two of your own domains take each other down:
$ dig +norec @127.0.0.11 example.test A +noall +authority +additional
example.test. 3600 IN NS ns1.example.test.
ns1.example.test. 3600 IN A 127.0.0.21 <- IN-DOMAIN glue: required
$ dig +norec @127.0.0.11 sibling.test A +noall +authority +additional
sibling.test. 3600 IN NS ns1.example.test.
ns1.example.test. 3600 IN A 127.0.0.21 <- SIBLING glue: optional
$ dig +norec @127.0.0.11 external.test A +noall +authority +additional
external.test. 3600 IN NS ns.hoster.net. <- OUT OF BAILIWICK: no glue possibleSibling-domain glue is optional. Some resolvers will not use it even when sent, so if a.example and b.example point at each other’s nameservers you are relying on a courtesy. Out-of-bailiwick names cost a second full resolution, which can itself fail. RFC 9471 (2023) requires a server to return all in-domain glue and to set the truncation bit rather than drop records, which matters now that the shipped EDNS buffer size is 1232 bytes.
Verify: run the hinge. The NS set should be exactly the set in your zone, every in-domain name should have an address beside it, and the DS, if you expect one, should be there with an RRSIG. Anything else is stage 1, fixed at a registrar rather than in a zone file.
Stage 2: The zone, the serial, and the copies
Your zone file is not the zone. It is one input to one of the copies, and the copy everybody reads is usually on a machine you do not own. The only thing connecting the copies is a 32-bit number in the SOA record.
It is not compared with >. It is compared with RFC 1982 serial arithmetic, in which “newer” means within half the number space: serial 5 is newer than 4294967290, and a serial you meant as a big increase can be a decrease. BIND prints the legal window when you get it wrong:
$ rndc reload example.test
zone reload queued
$ tail -2 named.log
zone example.test/IN: ixfr-from-differences: new serial (2026090302) out of range [2026090303 - 4173573949]
zone example.test/IN: not loaded due to errors.rndc reload reported success and the zone did not reload. The upper bound is the serial plus 231−1. The usual consequence is not an outage but a silent no-op, which the operator experiences as slow propagation.
A serial that goes backwards cannot be undone
Restoring a zone file from a backup is offered everywhere as a rollback. It is not one. With a journal present, an older serial makes the load fail, and named logs all zones loaded, logs running, exits 0, keeps serving its other zones, and answers SERVFAIL with no aa bit for this one:
$ dig +norec @127.0.0.21 example.test SOA
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 19203
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
$ grep -E "journal|not loaded|all zones loaded|running" named.log
zone example.test/IN: journal rollforward failed: journal out of sync with zone
zone example.test/IN: not loaded due to errors.
all zones loaded
runningWith the journal removed, it loads, and every secondary correctly refuses it. Both implementations, both right:
BIND: zone example.test/IN: serial number (2026090259) received from primary
127.0.0.21#53 < ours (2026090309)
Knot: [example.test.] refresh, remote 127.0.0.21@53, remote serial 2026090259,
remote is outdatedThe zone is now permanently split, primary old, secondaries new, and no amount of waiting fixes it. The escape is to move the serial forward past the highest one anywhere, never backwards.
NOTIFY
The mechanism that keeps secondaries current is the SOA REFRESH timer; NOTIFY only shortens the wait. REFRESH is clamped by the secondary, so a low value in your zone file does nothing: BIND’s built-in bounds are min-refresh-time 300 and max-refresh-time 2419200, with retry clamped to 500 and 1209600 seconds.
The list of who receives a NOTIFY is not configured. It is the zone’s own apex NS set, minus the server named in the SOA. With notify yes and no also-notify, two packets went out, to the two other servers the zone names:
23:42:44.200005 IP 127.0.0.21.32969 > 127.0.0.22.53: notify SOA? example.test.
23:42:44.699905 IP 127.0.0.21.51445 > 127.0.0.23.53: notify SOA? example.test.This is the mirror of the hinge. Query traffic comes from the parent’s list; NOTIFY comes from the child’s. A server missing from the first is invisible and starved; a server missing from the second is delegated and stale. What arrives is not reliably a delta: RFC 1995 lets a server answer IXFR with the whole zone, and BIND does whenever the delta exceeds max-ixfr-ratio, which on a signed zone is nearly always. One more BIND 9.18 default, printable with named -C: allow-transfer {any;}. Knot refuses by default.
The second hinge, for this stage. Ask every server the parent named for the SOA, and read two things — whether a line came back at all, and how the serials compare:
for ns in $(dig +norec @"$PARENT" "$DOMAIN" NS +short); do
printf '%-28s ' "$ns"; dig +norec @"$ns" "$DOMAIN" SOA +short
done- A blank line. That server is in the delegation and is not serving the zone. This is the branch nobody checks, and it is the worst one.
- Serials differ, and the secondary’s is lower. Ordinary lag. Find out whether the NOTIFY reached it.
- Serials differ, and the secondary’s is higher. The primary went backwards. They will never converge on their own.
- Serials all equal, and the data still differs. Two zones wearing the same number. Somebody edited a secondary.
Verify: run the second hinge. Every server the parent named should return a serial, all the same one. If a server returns nothing, go to stage 3: it is in the rotation.
Stage 3: Which copy answers
After a serial roll and one subsequent edit, the three servers the zone named were in two states, and all three set the authoritative-answer bit:
SERVER SERIAL www.example.test flags
127.0.0.21 6 203.0.113.55 qr aa
127.0.0.22 6 203.0.113.55 qr aa
127.0.0.23 2026090309 203.0.113.77 qr aaNo error anywhere, no log line anywhere. Forty queries through the two resolvers, cache flushed between each:
Unbound: 20 203.0.113.77
BIND: 11 203.0.113.55
9 203.0.113.77BIND is a coin flip. Unbound went to the stale server twenty times out of twenty. The likely mechanism is its infrastructure cache preferring the server with the best measured round-trip time, and the best server being the wrong one; that is a probable explanation, and the ratio is what was measured.
Now the operator, running the check every guide recommends:
$ dig @127.0.0.30 www.example.test +short # what the world is getting
203.0.113.77
$ dig +norec @127.0.0.21 www.example.test +short # what the operator checks
203.0.113.55dig @your-nameserver cannot detect any of the four commonest failures: a wrong delegation, a diverged secondary, a broken DS, and a cache that has not expired. It asks a machine you chose, and the resolvers do not.
The aa bit
aa distinguishes this server holds your zone from this server exists. A delegated server that has lost the zone answers without it; that is what “lame” means. Delegate a name to two servers, black-hole one and leave the other answering non-authoritatively:
BIND 20.02 s, then SERVFAIL
Unbound no answer at all inside 60 s
named.log: lame server resolving 'www.halflame.test' (in 'halflame.test'?): 127.0.0.11#53A fully black-holed delegation behaves the same way. A server that refuses the connection is dealt with in 0.02 seconds, so a firewall that drops packets is far worse than a nameserver that is switched off. BIND’s lame-ttl now defaults to 0: it re-learns that a server is lame on every query.
Verify: for every server the parent named, dig +norec @$ns $DOMAIN SOA +noall +comments and look for aa in the flags line. Not for an answer, for the flag.
Stage 4: The chain the parent vouches for
The DS record is a hash of one of your keys, published by a stranger, and nothing ever checks it against your zone. A zone with no DS is still a signed zone; every resolver ignores the signatures and returns the answer without the ad bit. The DS does not make the signatures exist. It makes them checkable, and therefore mandatory. Here the parent published a DS for key tag 24922 while the zone signed with 58884. All three authoritative servers answered in milliseconds:
$ for ns in 127.0.0.21 127.0.0.22 127.0.0.23; do dig +norec @$ns www.example.test +short; done
203.0.113.77
203.0.113.77
203.0.113.77
$ dig @127.0.0.30 www.example.test | grep -E "status:|EDE"
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 60501
; EDE: 6 (DNSSEC Bogus): (validation failure <www.example.test. A IN>: no keys have
a DS with algorithm ECDSAP256SHA256 from 127.0.0.22 for key example.test.
while building chain of trust)No aa check, no HTTP monitor and no dig @ns1 sees this. Only a validating resolver does: a large share of your users and none of your dashboards. Unbound 1.19.2 ships with ede: no; turning it on converts an opaque SERVFAIL into the RFC 8914 sentence above. BIND 9.18.39 returned no extended error for a bogus answer, and did for a stale one.
The ordering rule
The DS lives at the parent and carries the parent’s TTL, so every DNSSEC change is two changes on two clocks, and they must overlap. Publish the new DS alongside the old, wait a full DS TTL, move the signing, then remove the old DS. Never “remove, then change”: that takes the domain off the internet for everyone who has cached the old DS, and it is the sequence our own guide recommends.
Better, stop doing it by hand. CDS and CDNSKEY records publish your DS material in your own zone and let the registry take it from there (RFC 7344, 8078, 9615 and 9859). RFC 10026, published 23 July 2026 as BCP 246, tells registries to drop the DS TTL to five or fifteen minutes around a change and hold it there for a couple of days.
One event is not yours: on 11 October 2026 KSK-2024 becomes the key signing the root zone. It changes the trust anchor validating resolvers carry and requires nothing from a zone owner. A page telling you to prepare your zone for it has confused the root’s key with yours.
Verify: delv @<a validating resolver> $DOMAIN A prints fully validated or names the link that failed. Then compare the key tag in the parent’s DS, the first number in it, with dig +norec @$ns $DOMAIN DNSKEY | grep 257 piped through dnssec-dsfromkey -f -. Two numbers; they match, or your domain is down for everyone behind a validating resolver.
Stage 5: The clocks
Everything above happens instantly. What takes time is other people’s caches, and the corpus models that with one number, the TTL you set. There are six clocks. Two are yours, and neither governs either of the changes that take a domain down.
A negative answer lasts for min(SOA MINIMUM, the TTL of the SOA record itself, the resolver's own negative cap). DNS, All the Way Down says you need to lower both of the first two. It is a minimum: lowering either is sufficient. Measured with $TTL at 3600 and MINIMUM alone dropped to 60:
$ dig +norec @127.0.0.21 example.test SOA +noall +answer
example.test. 3600 IN SOA ns1.example.test. hostmaster.example.test. 2026090304 3600 900 1209600 60
$ dig +norec @127.0.0.21 stillnothere.example.test A +noall +authority
example.test. 60 IN SOA ns1.example.test. hostmaster.example.test. 2026090304 3600 900 1209600 60Same record, same server, same second, two different TTLs, because the authoritative server applies the min() itself before putting the SOA into a negative answer. Both resolvers cached the “no” for sixty seconds. Read the authority section, not the answer section.
| The clock | Who sets it | What it delays | Where you read it |
|---|---|---|---|
| The record’s TTL | you | a changed A, MX or TXT record | your zone file |
| The negative TTL | you, then the resolver | a name that did not exist when somebody asked | the authority section of an NXDOMAIN |
| The delegation NS TTL | the registry | every nameserver change | the hinge command, NS rows |
| The DS TTL, plus the validator’s bogus TTL | the registry, then the resolver | every DNSSEC change | the hinge command, DS row |
| The resolver’s ceiling and floor | whoever runs the resolver | everything you publish | you cannot — Unbound caps at 86400 positive and 3600 negative, BIND at 604800 and 10800 |
| The stale window | whoever runs the resolver | everything, after its TTL has expired | you cannot — up to 86400 where it is on |
Those values are documented software defaults, printable from named -C and unbound-control get_option. Per-TLD delegation TTLs are somebody else’s machine state; read your own with the hinge.
One zone with $TTL 604800 was cached by Unbound for 86400 and by BIND for 604800. The number in your zone file is a request. Serve-stale (RFC 8767) lets an expired record be served for up to a day. Both resolvers tested ship it off; the large public resolvers do not, so “wait one TTL” was never the rule. In BIND, stale-answer-enable yes alone does nothing, because stale-cache-enable defaults to false; with both on, a sixty-second record came back sixty-two seconds after expiry, carrying EDE: 3 (Stale Answer).
Verify: ask the resolvers your users use, without recursion, and read the TTL rather than the answer: dig +norecurse @1.1.1.1 www.example.com. A counting-down number is a cached copy and says how long is left. No answer means that resolver has not looked it up, the only completely good news at this stage.
Stage 6: When the change is over
There is no global check. The only evidence a change is finished is negative: the old servers stop being asked. A nameserver change takes effect at exactly the parent’s delegation NS TTL, not the answer’s TTL and not your zone’s own NS TTL. Here the answer’s TTL was 30 seconds, the zone’s NS TTL 3600 and the parent’s delegation TTL 120, and both resolvers switched at 120:
elapsed Unbound BIND
0s 203.0.113.28 203.0.113.28
...
105s 203.0.113.28 203.0.113.28
120s 203.0.113.29 203.0.113.29A DNSSEC fix takes effect at the DS TTL plus the validator’s own bogus-answer timer. With a 90-second DS TTL, a fix live on the authoritative servers at t=0 reached BIND at 90 seconds and Unbound at 140, the extra 50 being Unbound’s val-bogus-ttl, default 60:
0s Unbound=SERVFAIL BIND=SERVFAIL
90s Unbound=SERVFAIL BIND=203.0.113.10
140s Unbound=203.0.113.10 BIND=203.0.113.10The fix was complete before anyone could see it, and it arrived at different people at different times. At a real registry the DS TTL is usually hours, and you read it rather than choose it; the hinge prints it.
Of the six clocks, which were yours? If you lowered the record’s TTL and the negative TTL in advance, everything remaining is somebody else’s number. Read them rather than wait on them: the parent’s with the hinge, the resolvers’ with dig +norecurse. And watch the old nameservers’ query log: it is the only place in the system where “finished” is observable, because it is the only place the absence of a question is recorded.
Verify: when the old nameservers have had no queries for your zone for longer than the parent’s delegation TTL, the change has reached everyone who was going to ask. That is why the guides that say leave the old servers running are right, for a reason they do not give: they are your only instrument.
A worked diagnosis: the Friday migration
Back to the Friday migration from the top of the page: a new provider whose zone is byte-identical to the old one, checked record by record with dig @ns1.newprovider.net; nameservers changed at the registrar on Friday afternoon; the old provider left running, as every guide including this site’s own advises.
Over the weekend, tickets arrive from customers who cannot reach the site. Not all of them, and not the same ones twice. The site is up. Monitoring is green. dig from three laptops is fine, dig @1.1.1.1 is fine, both sets of nameservers answer, and traffic on the old servers is falling as expected. The hinge, run once:
$ PARENT=$(dig +short NS com. | head -1)
$ dig +norec +dnssec @"$PARENT" example.com A +noall +authority +additional
example.com. 172800 IN NS ns1.newprovider.net.
example.com. 172800 IN NS ns2.newprovider.net.
example.com. 86400 IN DS 41287 13 2 <the OLD provider's key>
example.com. 86400 IN RRSIG DS 13 2 86400 ...The NS set is right. The DS is not: it fingerprints the old provider’s key signing key, and the new provider signs with its own. Nobody removed it, because nobody at either provider owns it. The DS lives at the registry, and the only party who can change it is the registrant, through a form most people have never opened.
It is intermittent rather than total because of stage 6. Resolvers pick up the new delegation as their own copy of the referral expires, and here that TTL is 172800, two days. The failure spreads, resolver by resolver, across a weekend, and the population affected is precisely the population behind a validating resolver.
The obvious fix makes it worse. “Turn DNSSEC off”, removing the DS, is what our own guide says, and it buys another 86400 seconds of the identical failure for everyone who has cached the bad one. The correct sequence is RFC 10026’s: publish the new DS alongside the old, wait a DS TTL, remove the old one, or hand the registry a CDS record and let it do the ordering.
Stage 4 says the DS is wrong. Stage 6 says why it is intermittent and when it ends, and an engineer who fixes the DS and goes home on Saturday gets the same tickets on Sunday. The operator owned every byte of both zones and was wrong about neither; the broken record was published by a party they had never spoken to, on behalf of a provider they had just left.
Symptoms, and which stage owns them
| What you see | Stage | What to run, or what it is |
|---|---|---|
| A new nameserver is live and receiving no traffic at all | 1 | it is in your zone and not in the parent’s delegation |
| A nameserver you removed is still being queried | 1, 6 | the parent’s delegation NS TTL, which you did not set |
| The domain is dead for some resolvers and fine for others, with every server healthy | 4 | the DS does not match the key in the zone; delv names the link |
Two of your nameservers give different answers, both with aa | 2, 3 | compare serials; then find out which one your resolver prefers |
| A secondary’s serial is higher than the primary’s | 2 | the primary went backwards; they will never converge |
rndc reload succeeded and the change is not visible anywhere | 2 | the serial is not greater under RFC 1982; the zone did not load |
| Resolution takes twenty seconds and then fails | 3 | a lame or black-holed server in the delegation |
| You added a record and it still does not exist | 5 | the negative answer cached before you added it |
| A record is still being served after its TTL expired | 5 | serve-stale, for up to a day |
dig $DOMAIN NS disagrees with your registrar | 1 | it is showing the child’s list; the parent’s is the one used |
| Everything above is correct and a resolver still gives the old answer | 6 | nothing is broken; read the clocks rather than waiting on them |
Before you call it done
- Run the hinge against your own domain and read the NS set, the glue and the DS.
- Write down the parent’s NS TTL and DS TTL. They decide how long your next change takes, and you cannot edit either.
- Ask every server the parent named for the SOA: each one answers, sets
aa, and returns the same serial. - If you are signed, compare the DS key tag with your own key;
delvagainst a validating resolver is the one-command version. - Before any nameserver or DNSSEC change, write the order down: the new server holds the zone before the delegation moves; the new DS is published before the old one is removed. Overlap, never replace.
Related reading
- DNS, All the Way Down — the eight stages on the client’s side of the same wire, and the page this one hands the negative-caching arithmetic back to.
- Getting a Domain and Its DNS Right — buying the domain and choosing the records, and the page whose DNSSEC instruction this one exists to finish.
- Server Migration — the other half of the weekend this page’s worked diagnosis happens on.

