Somebody has to write the answers down. A zone is a text file of records, one server holds the master copy, and several others keep exact copies so the zone survives when any one of them fails. This chapter is about that file, the SOA record that versions it, and the protocol that keeps the copies in step. If the DNS tree, records and caching are new to you, start with networks · DHCP & DNS; this picks up where it stops.
Common mix-up: "primary" and "secondary" are about where the data comes from, not about who answers first. A resolver sees only the zone's NS records and treats every listed server as an equal authority; it does not look for the primary and it does not prefer it. The primary is often not listed in NS at all. What keeps the answers identical is the transfer machinery in this chapter, and when it breaks, different servers give different answers.
A zone is the part of the DNS tree that one set of servers is authoritative for: a domain, minus any subdomains that have been delegated away to someone else. Chapter 1 covers where those cuts fall. This chapter is about what happens inside one zone, starting with how it is written down.
The original format is the master file, defined in section 5 of RFC 1035 in 1987 and still read by BIND, NSD, Knot and PowerDNS today. People also call it a zone file. It is plain text, one record per line, and it is built for typing: anything you leave out is filled in from context. That makes zone files short. It also makes them the source of one of the oldest bugs in the DNS.
Here is a small zone, written the way an administrator would write it. It is simulated: every name and address on this page comes from the ranges reserved for documentation.
; example.com, as the admin typed it (simulated) $ORIGIN example.com. $TTL 1h @ IN SOA ns1 hostmaster ( 2026102201 ; serial 2h ; refresh 15m ; retry 2w ; expire 1h ) ; minimum: negative-caching TTL IN NS ns1 IN NS ns2.example.net. IN MX 10 mail ns1 A 192.0.2.53 www 300 A 192.0.2.10 mail A 192.0.2.25
Five shortcuts are at work, and each one is a rule you can state exactly.
ns1 means ns1.example.com. and ns2.example.net. means itself.example.com., the apex of the zone.1h and 2w are a widely supported BIND convention; the protocol itself only carries seconds.Parentheses let one record run over several lines, and a semicolon starts a comment that runs to the end of the line. That is why the SOA above can carry a label on each of its numbers.
RFC 1035 defines two directives, $ORIGIN and $INCLUDE. $INCLUDE pulls in another file, optionally with its own origin, and the origin goes back to what it was when that file ends. RFC 2308 added $TTL. BIND adds a few of its own, such as $GENERATE for numbered runs of records; other servers may not accept them, so a file that uses them is no longer portable.
Before $TTL, a record with no TTL took the last TTL stated explicitly in the file, and the class likewise defaults to the last one stated. The TTL and class may come in either order after the owner, which is why a parser has to look at each token and decide which it is.
Not every zone lives in a text file. Many servers keep zones in a database and export or import master-file text only when asked, and hosted DNS providers show you a web form. The format still matters because it is what dig prints, what a transfer reproduces, and what you paste into a support ticket. Chapter 5 covers what each record type means; this chapter only cares how they are written and copied.
Type or paste a zone. The parser below applies the master-file rules as you type: it joins parenthesized lines, drops comments, follows $ORIGIN and $TTL, fills blank owners from the line above, decides which token is the TTL and which the class, and completes every relative name in the owner and in the data. The output is what the server actually loads, written fully qualified the way dig would print it. The part of each name that came from the origin is shown in blue; a name that probably lost its trailing dot is shown in magenta with a warning. The zone is simulated and nothing leaves your browser.
The "probably missing a dot" check is a heuristic, the same idea linting tools use: a relative name that already ends in the zone's own name, or in a familiar top-level label such as com or net, almost certainly was meant to be absolute.
Every zone has exactly one SOA record, at its apex. It names the zone's source and carries the numbers that drive replication. Ask any of the zone's servers for it:
$ dig +noall +answer example.com SOA (simulated output) example.com. 3600 IN SOA ns1.example.com. hostmaster.example.com. 2026102201 7200 900 1209600 3600
| Field | Value here | What it controls | Who reads it |
|---|---|---|---|
| MNAME | ns1.example.com. | The primary server: the original source of the zone's data | Secondaries sending NOTIFY, dynamic update clients (RFC 2136). Resolvers ignore it. |
| RNAME | hostmaster.example.com. | A mailbox for the person responsible. The first dot stands for @: this is hostmaster@example.com. A dot inside the local part is escaped as \. | Humans |
| SERIAL | 2026102201 | The zone's version number. Secondaries copy the zone only when it goes up | Secondaries |
| REFRESH | 7200 (2 h) | How often a secondary asks the primary for the SOA to see whether the serial has changed | Secondaries |
| RETRY | 900 (15 min) | How long to wait before asking again after a refresh fails | Secondaries |
| EXPIRE | 1209600 (14 days) | How long a secondary keeps answering from its copy without a successful refresh. After that it stops answering for the zone | Secondaries |
| MINIMUM | 3600 (1 h) | Today: the TTL for negative answers, so how long resolvers may cache "no such name." Negative caching is Chapter 4 | Resolvers (RFC 2308) |
The first two fields are names and the last five are unsigned 32-bit numbers. Three of the timers, REFRESH, RETRY and EXPIRE, are only for secondaries; resolvers never look at them. The fourth, MINIMUM, is the one that changed meaning over the years, and it is the only one that reaches the wider internet.
RFC 1912, the old but still useful guide to common DNS errors, gives the reasoning for each value. REFRESH is how long you can live with a secondary serving out-of-date data. RETRY is usually a fraction of REFRESH. EXPIRE should be longer than any outage you expect, so a secondary survives a long one, and it must be longer than RETRY and MINIMUM. Once NOTIFY is in place, REFRESH matters much less, because changes no longer wait for it.
RFC 1035 defined MINIMUM as the minimum TTL for every record in the zone. Servers never really enforced that. In practice it was used as the default TTL for records that did not give one, and some software used it as the TTL for negative answers too. RFC 2308 (1998) settled it: MINIMUM is the TTL of negative answers, and the default record TTL moved to the new $TTL directive. A resolver caches a negative answer for the smaller of MINIMUM and the SOA record's own TTL.
That is why a file without $TTL is worth fixing. Different servers handle the missing default differently, and some fall back to MINIMUM with a warning in their log, which may be much shorter or longer than you intended.
The serial is a version number, and the rule a secondary follows is simple: if the primary's serial is greater than mine, my copy is old, so fetch the new one. Change the zone without raising the serial and no secondary will ever fetch the change. That is the most common replication bug there is, and Instrument 2 lets you commit it.
"Greater than" needs care, because the serial is only 32 bits and it is allowed to wrap round. RFC 1982 defines serial number arithmetic for this. Picture the 4,294,967,296 possible serials around a circle. From any serial, the half of the circle ahead of it counts as newer and the half behind counts as older. A serial exactly half way round is neither: the comparison is undefined, and software may do anything.
Two conventions are common for choosing serials. Some people simply add 1 each time. Many use the date: YYYYMMDDnn, so 2026102201 is the first change on October 22, 2026 and 2026102202 is the second. Dates sort correctly, tell you when the zone last changed, and still fit in 32 bits until the year 4294. The scheme's one weakness is that it allows only 100 changes a day; zones changed by software often switch to a counter or to the Unix time in seconds.
The circle explains the classic accident. Someone means 2026102201 and types 2062102201. The secondaries copy it, because it is newer. Fixing the typo by loading 2026102202 does nothing: from 2062102201, the number 2026102202 is behind, so every secondary decides it already has a newer zone and ignores the fix. You cannot go backward. You go forward round the circle, in steps of less than half.
dig +nssearch.Take 0 and 2,147,483,648. Going forward from 0 to the other is exactly half the circle, and going forward from the other back to 0 is also exactly half. Any rule that called one of them greater would, applied to the pair the other way round, call the other one greater too, so the answer would depend on which number you asked about first. RFC 1982 chose to leave the case undefined rather than pick a rule that is inconsistent. That is also why the largest safe step is 231 − 1, one short of half.
RFC 1982 also defines only addition, and only of numbers up to that limit. You never subtract from a serial, and you never compare serials that are more than half the circle apart and expect a sensible answer. A secondary that was switched off for a very long time while the serial raced ahead is one of the rare places that could happen.
Compare mode: enter the serial a secondary holds and the serial the primary now has. The circle shows both, with the half that counts as newer than the secondary's serial shaded, and the verdict is the one a secondary following RFC 1982 would reach. Recover mode: enter a serial that went out too high and the serial you wanted; the calculator plans the steps, checks each one against RFC 1982, and draws the walk round the circle.
Every zone should have at least two authoritative servers, and usually has more. One of them, the primary, holds the zone as the administrator edits it, from a file, a database or a provider's API. The others are secondaries: they load the zone from the primary over the DNS protocol itself and answer from their copy. Older documents call them master and slave; RFC 9499, the current DNS terminology reference, uses primary and secondary. A secondary can also feed further secondaries, so the source a server copies from is just "its primary."
The original sync loop is polling, and it runs on the SOA timers. Every REFRESH seconds the secondary asks the primary a single small question, the SOA of the zone, and compares serials. Same serial: nothing to do, try again in REFRESH. Bigger serial: transfer the zone. No answer: try again in RETRY, and keep trying. If no refresh has succeeded for EXPIRE seconds, the copy is considered too old to be authoritative, and the secondary stops answering for the zone. Resolvers that ask it then get SERVFAIL, and fall back to other servers if they can.
Polling alone means a change can take up to a whole REFRESH interval to reach a secondary. RFC 1996 fixed that in 1996 with NOTIFY: when the zone changes, the primary sends each secondary a short message saying the zone may have changed. The secondary then does exactly what it would have done when its timer fired, asks for the SOA and compares, so a forged or stale NOTIFY can at worst cause an unnecessary check. The primary may wait a short random time before notifying, so a hundred secondaries do not all ask at once. Polling stays on underneath as the safety net for lost messages.
| Mechanism | Defined in | What crosses the wire | When it is used |
|---|---|---|---|
| SOA query | RFC 1034 / 1035 | One question, one record back | Every refresh and after every NOTIFY, to compare serials |
| AXFR | RFC 5936 (first in RFC 1034/1035) | The whole zone, SOA first and SOA last, over TCP | First load, or when the difference is not available |
| IXFR | RFC 1995 | Only the differences between two serials: records deleted, then records added, for each version step | Routine changes to a large zone |
| NOTIFY | RFC 1996 | "Zone X may have changed," plus the new SOA as a hint | Right after the primary loads a new serial |
AXFR copies everything. The response is the zone's SOA, then every other record, then the SOA again so the receiver knows it has the end. RFC 5936 pinned down the details that RFC 1035 left loose: AXFR runs over TCP, and the response may be split across many messages. IXFR sends only what changed. The secondary includes the serial it has; the primary replies with the deletions and additions that lead from that serial to the current one, or, if it no longer remembers that far back, falls back to a full transfer. For a zone with a million records and ten changes a day, the difference is enormous.
A popular design is the hidden primary. The primary sits somewhere private and is not in the zone's NS records, so no resolver ever queries it. It exists only to feed the secondaries, which are the public face of the zone. Signing keys, admin tools and the database stay off the internet, and an attack on the public servers cannot reach the source of truth. RFC 1996 calls a server like this a stealth server; RFC 2182 discusses the same idea.
An IXFR response is a sequence of SOA records used as fences. It opens with the current SOA. Then, for each change from the secondary's version forward, it gives the old SOA followed by the records to delete, and the new SOA followed by the records to add. It closes with the current SOA again. If the change log is gone, the server may answer with what is in effect a full AXFR instead; the client can tell by the shape of the answer.
; IXFR from 2026102201 to 2026102202 (simulated, shortened) example.com. SOA ... 2026102202 ... ; current version example.com. SOA ... 2026102201 ... ; delete these from 01: www.example.com. 300 A 192.0.2.10 example.com. SOA ... 2026102202 ... ; add these to make 02: www.example.com. 300 A 192.0.2.11 example.com. SOA ... 2026102202 ... ; end
Dynamic update (RFC 2136) is the other way records reach a primary: a client sends add and delete instructions as a DNS message, and the primary applies them, bumps the serial and notifies its secondaries. Laptops and DHCP servers registering names on a local network is the common case, covered in Chapter 10.
Three simulated servers for example.com: the primary ns1 and the secondaries ns2 and ns3. The secondaries really poll on REFRESH, fall back to RETRY when the primary does not answer, and stop answering when EXPIRE runs out; NOTIFY really triggers an immediate check. Each lane shows what that server would answer over time (up to the last eight simulated hours): blue for the current zone, orange for a stale copy, magenta for down or SERVFAIL. Try editing without bumping the serial, or taking the primary down and waiting. The timers are minutes so the story fits on screen; real zones use hours and weeks.
The original DNS assumed that anyone could copy any zone. A zone transfer was a convenience for operators, and the data was public anyway, since anyone can look up any single name. The difference is scale. One lookup answers one question; one AXFR hands over the complete inventory of the zone: every host name, every internal-sounding label, every mail server and test machine, without any guessing. RFC 5936 says plainly that administrators may restrict AXFR, to keep the bulk zone private or to protect the server from the load.
The defensive picture is simple. An authoritative server should refuse AXFR and IXFR from everyone except the secondaries that need them, and every server software's default is now to refuse or to require configuration. Checking your own zone is one command, run from a network that is not one of your secondaries: the answer you want is a refusal.
$ dig axfr example.com @ns2.example.net (your own zone, from outside; simulated) ; <<>> DiG <<>> axfr example.com @ns2.example.net ; Transfer failed. ← what you want to see
There are two layers of protection, and good setups use both.
TSIG proves who is talking and that nothing was changed, but it does not hide the zone from someone watching the network path. RFC 9103 (2021) adds zone transfer over TLS, known as XoT, for zones where the contents themselves are sensitive. DNSSEC is a different tool for a different problem: it lets resolvers verify the records they receive, but it does not decide who may copy the zone.
A TSIG key has a name, which both ends must agree on exactly, an algorithm, and a random secret. Tools such as BIND's tsig-keygen generate one; you put the same block in both servers' configuration and tell each to use it for that peer. The example below is a shape, not a key: the secret is a placeholder.
// primary and secondary both get this block (illustration only) key "ns1-ns2.example.com" { algorithm hmac-sha256; secret "<base64 secret generated on your own server>"; };
Treat the secret like a password: keep it out of version control and out of zone files, give each pair of servers its own key so one leak does not unlock everything, and replace keys when people with access leave. A key shared by every server in an organization turns one compromise into all of them.
TSIG is symmetric: both ends hold the same secret, so either one could forge the other's messages. That is fine between two servers run by the same people or by contract partners. It is why DNSSEC, which must be checkable by strangers, uses public-key signatures instead.
Secondaries exist so that one failure does not take the zone off the internet. That only works if they do not share the failure. RFC 2182, written in 1997 to guide the choice of secondaries, puts it directly: secondary servers must be placed at both topologically and geographically dispersed locations, to minimise the likelihood of a single failure disabling all of them. It specifically warns against putting all of a zone's servers on the same LAN segment in the same room, and it applies the same logic to a single network provider. Two servers behind one router, one power feed or one upstream provider are, for this purpose, one server.
The modern version of the same mistake is putting every NS record with one DNS provider. Many large organizations now list name servers from two independent providers, which works with the primary-and-secondary model in this chapter as long as both providers can transfer the zone from a common source, or are both fed by the same automation. Chapter 9 covers anycast, which lets one server name stand for many machines; it helps, but one provider's anycast cloud is still one provider.
Then there is the delegation itself. A zone's name servers are listed twice: once in the parent zone, as the delegation NS records (plus glue addresses when needed), and once in the zone's own apex, the child NS set. Resolvers follow the parent's list to find the zone and then often prefer the child's. The two lists are meant to match, and when they drift apart, failures become intermittent and hard to reproduce: it depends which list a given resolver happens to have cached.
A lame delegation is a server that is listed as authoritative for a zone but does not actually serve it. Ask it and it answers with a refusal, a referral elsewhere, an answer without the authoritative flag, or nothing at all. It usually happens quietly: a zone moves to a new provider and the old servers are never removed from the parent, a secondary is decommissioned but still listed, or a typo in an NS record points at a machine that has never heard of the zone. Resolvers waste time on the lame server and fall back to the others, so the zone limps along slowly until the day the working servers fail too.
; compare every listed server's SOA in one go (simulated output) $ dig +nssearch example.com SOA ns1.example.com. hostmaster.example.com. 2026102202 7200 900 1209600 3600 from server 192.0.2.53 in 11 ms. SOA ns1.example.com. hostmaster.example.com. 2026102202 7200 900 1209600 3600 from server 198.51.100.53 in 24 ms. SOA ns1.example.com. hostmaster.example.com. 2026102201 7200 900 1209600 3600 from server 203.0.113.53 in 87 ms. ← one secondary is behind
| Failure | Symptom | How to see it | Fix |
|---|---|---|---|
| Serial not raised | Some servers give the new answer, some the old, indefinitely | Same serial everywhere, different data | Raise the serial on every change; let tooling do it |
| Serial set too high | Later fixes never reach the secondaries | Secondaries' serial is ahead of the one you load | Step forward round the circle (Instrument 3) |
| Transfers blocked | A secondary falls behind, then SERVFAILs after EXPIRE | dig +nssearch shows different serials | Check the allow list, TSIG key name and clock |
| Lame delegation | Slow or intermittent lookups | Ask each listed server directly with recursion off: no authoritative answer | Remove it from the parent and child NS sets, or make it serve the zone |
| Parent and child NS differ | Behavior varies by resolver and by time | Compare the parent's delegation with the zone's own NS | Update the registrar's delegation and the apex NS together |
| No diversity | One outage takes the zone off the internet | All NS addresses in one network or one provider | Add a secondary elsewhere (RFC 2182) |
| Open AXFR | The whole zone can be listed by anyone | dig axfr against your own servers from outside | Allow transfers only to secondaries, with TSIG |
To see the parent's view, ask one of the parent zone's servers for your NS records with recursion turned off, dig +norec; the answer arrives in the authority section as a referral. To see the child's view, ask each of your own servers the same question. Each listed server should answer with the authoritative-answer flag (aa) set, the same NS set and the same serial. Chapter 14 walks through reading those answers line by line.
DNSViz (dnsviz.net) does this for a whole delegation chain at once and draws it as a graph, flagging lame servers, NS mismatches, missing glue and DNSSEC errors. It is a good first stop when a zone "works for me" but not for someone else, and RFC 9471 (2023) now spells out what glue authoritative servers must include in referrals.