is it dns? / wiki ← back to the live monitor

🔁 Zone transfer: AXFR / IXFR / SOA

Zone transfer is how a zone's data moves between servers. A primary owns the records; secondaries pull them. The SOA serial number is the heartbeat: when it bumps, secondaries notice and refresh. AXFR always uses TCP; ordinary queries are UDP-first and fall back to TCP on truncation. Syslog tells you something interesting here, and one stale field can keep your secondary running yesterday's data for days.

The cast of characters#

TermMeaning
Primary (master)Authoritative server that holds the editable master copy
Secondary (slave)Authoritative server that pulls the zone from the primary
NOTIFYRFC 1996 push: primary nudges secondaries the instant a zone changes. See query-types for DNS opcodes.
SOA serialA 32-bit integer; bumping it tells secondaries "there's new data"
AXFRFull zone transfer: every record, every time. Used for first sync and resyncs.
IXFRIncremental transfer: only records that changed since serial N. RFC 1995.
TSIGShared-key authentication for transfers: RFC 8945 (obsoletes RFC 2845 and RFC 4635). See also ntp (clock skew breaks TSIG).

The dance:

       primary                              secondary
       ------                               ---------
       record change committed
       SOA serial bumps         --NOTIFY--> "you should refresh"
                                            |
                                            +--SOA query--> primary
                                                            "serial = N+1"
                                            received N before
                                            so secondary requests:
                                            --IXFR from serial=N--> primary
                                                            sends delta
                                            applied, serial now N+1

If IXFR can't satisfy the gap (e.g., secondary too far behind), the secondary falls back to AXFR.

The SOA serial: the heartbeat#

Every commit to an auth zone increments the SOA serial. Without that increment, secondaries don't know anything changed and won't pull.

Most modern authoritative servers handle the bump automatically: any record-mutation API or UI commit increments the SOA. You generally don't maintain a serial by hand.

To watch the serial, query a real zone's SOA. Here is isitdns.net as a live example:

dig @1.1.1.1 isitdns.net SOA +short
coleman.ns.cloudflare.com. dns.cloudflare.com. 2409741473 10000 2400 604800 1800

The third field (2409741473) is the serial. This is a snapshot; the value you see on your run will differ. After any record change the serial will be higher.

nslookup -type=SOA isitdns.net 1.1.1.1
Server:		1.1.1.1
Address:	1.1.1.1#53

Non-authoritative answer:
isitdns.net
	origin = coleman.ns.cloudflare.com
	mail addr = dns.cloudflare.com
	serial = 2409741473
	refresh = 10000
	retry = 2400
	expire = 604800
	minimum = 1800

nslookup labels each SOA field, which is helpful for learning the structure. dig +multiline does the same with inline comments:

dig @1.1.1.1 isitdns.net SOA +multiline
isitdns.net.		1800 IN SOA coleman.ns.cloudflare.com. dns.cloudflare.com. (
			2409741473 ; serial
			10000      ; refresh (2 hours 46 minutes 40 seconds)
			2400       ; retry (40 minutes)
			604800     ; expire (1 week)
			1800       ; minimum (30 minutes)
			)

nslookup has no +dnssec equivalent, so it cannot request the RRSIGs that accompany a signed SOA. Use dig +dnssec for DNSSEC inspection.

The serial format that survives manual edits cleanly is YYYYMMDDNN, the syntax RFC 1912 §2.2 recommends. Internal schemes (epoch-style, counter-based) are also fine. RFC 1982 serial-number arithmetic governs comparison: "bigger = newer" with proper wrap-around math is the contract. See rfc-index for a pointer to RFC 1982. RFC 1982 comparison is defined only when the difference is less than 2^31; serials differing by exactly 2^31 have undefined ordering. Avoid serial values near wrap-around boundaries during manual edits.

dig for transfers: AXFR and IXFR#

dig @<primary> <zone> AXFR

The primary must allow the transfer from your IP (see "Allow / restrict transfers" below). dig's AXFR output is one record per line, ending with the SOA repeated. RFC 5936 §2.2 requires the transfer to conclude with the same SOA record it opened with; that repeated SOA is the end-of-zone marker.

dig @<primary> <zone> IXFR=<serial>

Provide the serial your secondary currently holds. If the primary cannot satisfy the incremental gap, it responds with a full AXFR instead.

nslookup has no portable IXFR capability. For AXFR, some implementations accept -type=AXFR, but support and output format vary by platform. Use dig for reliable transfer testing.

nslookup can't show IXFR or inspect transfer wire details. Use dig.

What the DNS header flags look like#

Transfer queries are normal DNS messages but the header flags tell you what kind:

Query typeopcodeqraa (on response)Transport
SOA pollQUERY (0)0/11 if server is authoritative (primary or secondary)UDP or TCP
IXFRQUERY (0)0/11UDP-first per RFC 1995 §2; falls back to TCP when the response overflows. In practice most secondaries just use TCP.
AXFRQUERY (0)0/11TCP always: entire zone wouldn't fit in UDP
NOTIFYNOTIFY (4)0/1n/aUDP (TCP fallback)

You can see the opcode in any dig response. A SOA query shows the same opcode: QUERY the transfer request uses:

dig @1.1.1.1 isitdns.net SOA
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51141
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

The ad flag appears because isitdns.net is DNSSEC-signed and 1.1.1.1 validates the answer; an unsigned zone shows only qr rd ra. An AXFR request produces the same opcode: QUERY header. The difference is the qtype in the question section and the multi-message TCP stream in the answer.

nslookup does not expose opcode or flag fields. Use dig for wire-level inspection.

For NOTIFY specifically you won't normally see it from dig (it's primary→secondary), but a capture will show opcode: 4.

What syslog shows#

Most authoritative DNS servers log transfer events in a recognizable shape. A typical line looks like:

client 198.51.100.42#12345 (example.com): transfer of 'example.com/IN': AXFR started (serial 2024071701)
client 198.51.100.42#12345 (example.com): transfer of 'example.com/IN': AXFR ended

Recent BIND also appends transfer statistics (messages, records, bytes, seconds) to the ended line.

Key fields:

FieldWhat it tells you
clientWho asked for the transfer (IP + ephemeral port)
transfer of '<zone>/IN'Which zone and class
AXFR / IXFRWhich type
started / endedBookends for the conversation

If the transfer was denied:

client 198.51.100.42#12345 (example.com): zone transfer 'example.com/IN' denied

denied is the line you grep for when "my secondary stopped syncing" lands in the queue. It tells you the ACL is the cause.

In dnstap the transfer request reaching an authoritative server appears as an AUTH_QUERY message with qtype set to AXFR or IXFR (message types defined in dnstap.proto); whether the multi-message response stream is also captured varies by server. See wireshark-dns-filters for capture-level inspection of DNS traffic.

Allow / restrict transfers#

By default a well-configured authoritative DNS server refuses outside zone transfers. To open one to a specific peer, you add the peer's IP, a TSIG key, or both to the zone's allow-transfer ACL (or whatever the equivalent is called in your DNS server). Commercial appliances expose this as an ACL field on the zone object; managed DNS providers handle secondaries through a dashboard pairing flow rather than open-Internet AXFR.

Use TSIG for anything crossing administrative boundaries. An IP address is trivially spoofable on a misconfigured network. TSIG clock skew breaks authentication; keep NTP synchronized on every server involved (see ntp).

Real-world scenarios#

ScenarioMechanism
Internal replication inside a clusterMost vendors handle this internally; you don't expose AXFR
dig AXFR test from a known hostAdd the host's IP to the zone's allow-transfer
External secondary (a partner pulls your zone)TSIG-keyed entry in allow-transfer, share the key out-of-band
Migrating a zone to a new providerTemporarily allow their IP / TSIG, they AXFR, then cut NS

SOA timers: the knobs that govern transfer behavior#

The SOA RDATA contains four timers (and a serial); they control transfer cadence and freshness:

FieldWhat it doesReasonable default
refreshHow often a secondary checks the primary's SOA (when NOTIFY is not trusted)3600 (1h)
retryIf a refresh failed, when to retry900 (15m)
expireIf the primary stays unreachable this long, the secondary stops serving the zone604800 (1w)
minimum (negative TTL)How long resolvers may cache NXDOMAIN answers1800 (30m)

The SOA record itself also has a TTL (set at the zone level, outside the RDATA), which governs how long other resolvers may cache the SOA record.

Pulling minimum down before a change avoids long NXDOMAIN-caching pain when you add a record that previously didn't exist. See soa for a full walkthrough of each timer and the SOA record structure.

Gotchas#

  • AXFR is TCP-only. A firewall that blocks TCP/53 (only allows UDP/53) breaks transfers silently. See anatomy-of-a-query for the TCP vs UDP decision logic.
  • NOTIFY without authentication is spoofable. Well-configured secondaries accept NOTIFY only from their configured masters; check also-notify / masters on each side.
  • TSIG clock skew. TSIG signs requests with a timestamp tolerance ("fudge") of 300 seconds recommended by RFC 8945 §10. NTP drift beyond that breaks transfers. See ntp.
  • IXFR fallback to AXFR is silent in most server logs unless you turn up verbosity. If transfers feel slow, check whether they're falling back.
  • A stub zone is not a secondary. Stubs copy NS + SOA (plus glue) only; they do not AXFR/IXFR the full zone. See stub.
  • Never trust the allow-transfer default. BIND 9.18 and earlier default to allowing transfers to any host; BIND 9.20 changed the default to deny (9.20.0 release notes). NSD and Knot deny without an explicit ACL; PowerDNS allows localhost only. Set the ACL explicitly on every zone: an open AXFR hands your entire zone to anyone who asks.
  • Dynamic update (RFC 2136) is different. DDNS updates the zone in-place and bumps SOA, but is unrelated to AXFR/IXFR, those are server-to-server. See ddns.

See also#

  • auth: what owns the master copy
  • soa: apex metadata in full
  • ns: the NS records that say who can serve
  • ddns: dynamic update, the other way zone data changes