A secondary BIND server’s zone transfers are failing. The zone is still being served, clients are still getting answers, and your health checks are still green. The secondary is serving stale data from the last successful transfer, and the SOA expire timer is counting down. When it reaches zero, the secondary stops serving the zone and returns SERVFAIL or REFUSED for every query.
Depending on the zone’s SOA values, you may have hours, days, or weeks of runway before the zone goes dark, with no visibility into the countdown unless you are specifically tracking it.
This article covers how to read the xfer-in log category, diagnose why transfers are failing, and compute your actual expire runway before the zone disappears.
What this means
BIND uses two zone transfer mechanisms, both over TCP port 53:
- AXFR (full transfer): the entire zone is sent from primary to secondary. Used for the initial load and as a fallback when IXFR fails or is unavailable.
- IXFR (incremental transfer): only the differences between the current serial and the secondary’s serial are sent. Requires a valid journal on both sides.
A secondary requests IXFR by default (request-ixfr yes). If the primary does not support IXFR or cannot compute a delta from its journal, the secondary falls back to AXFR. Occasional fallback is normal. Frequent AXFR fallback indicates a problem: journal corruption, a serial gap too large for IXFR, or the primary lacking sufficient IXFR history.
When a transfer fails, BIND logs it under the xfer-in category. The failure is invisible to clients because the secondary continues serving the zone from its last successful copy. The zone’s SOA record contains the timers that govern this grace period:
- refresh: how often the secondary checks the primary’s serial
- retry: how long to wait before retrying after a failed refresh
- expire: how long the secondary will continue serving stale data before giving up entirely
The expire value is the hard deadline. Once it passes without a successful transfer, the secondary removes the zone.
flowchart TD
A["Transfer fails
logged in xfer-in"] --> B["Secondary serves
last-known copy"]
B --> C["SOA refresh timer
triggers retry"]
C --> D{"Transfer succeeds?"}
D -- Yes --> E["Zone in sync
serials match"]
D -- No --> F["SOA retry timer
triggers another retry"]
F --> G{"Expire timer
reached?"}
G -- No --> C
G -- Yes --> H["Zone removed
SERVFAIL for all queries"]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Primary unreachable | Connection timeout or refused in xfer-in logs; primary may be cached as unreachable | dig +time=2 @primary-ip zone SOA |
| Firewall blocking TCP/53 | UDP SOA queries work but TCP fails; connection reset or timeout on transfer | dig +tcp +time=2 @primary-ip zone SOA |
| TSIG key mismatch | Authentication failure or bad signature in xfer-in logs | Compare key names, algorithms, and secrets on both servers |
| Journal corruption on secondary | IXFR repeatedly fails and falls back to AXFR; every refresh does a full transfer | Check .jnl file integrity |
| BIND 9.20.0 allow-transfer default | All transfers fail after upgrading the primary; no explicit ACL | Check allow-transfer on the primary |
| Primary overloaded (tcp-clients) | Intermittent failures; primary logs quota exhaustion on TCP clients | Check tcp-clients limit and concurrent load on primary |
| Secondary transfer quota full | Transfers deferred, not failed; quota message in xfer-in logs | Check transfers-in (default 10) and transfers-per-ns (default 10) |
Quick checks
# Compare SOA serials - third field in each output is the serial
dig @primary-ip example.com SOA +short
dig @127.0.0.1 example.com SOA +short
# Check zone state on the secondary
rndc zonestatus example.com
# Check recent xfer-in log entries
journalctl -u named --since "1 hour ago" | grep -iE "xfer|transfer"
# Test TCP connectivity to primary on port 53
dig +tcp +time=2 +tries=1 @primary-ip example.com SOA
# Check transfer-related counters via statistics channel
# <!-- TODO: verify actual zonestats counter names in BIND JSON stats; XfrFail/XfrSuccess may not exist -->
curl -s http://127.0.0.1:8053/json/v1/server | \
python3 -c "import sys,json; d=json.load(sys.stdin); \
[print(f'{k}: {v}') for k,v in d.get('zonestats',{}).items()]"
# Verify allow-transfer ACL on primary
named-checkconf -p | grep -A3 allow-transfer
# Check current transfer activity on the secondary
rndc status | grep -i transfer
How to diagnose it
Confirm the serial mismatch. Compare the SOA serial on the primary and the secondary. If they differ, the secondary is behind. Use
dig @primary-ip zone SOA +shortanddig @127.0.0.1 zone SOA +shortand compare the third field.Compute your expire runway. Run
rndc zonestatus zoneon the secondary. If the zone is close to expiry and you have no other signal, treat any persistent serial mismatch lasting longer than the refresh interval as urgent.Read the xfer-in logs. The
xfer-inlog category records incoming transfer attempts and their outcomes. Look for the specific failure mode: connection refused, timeout, authentication failure, FORMERR, or quota deferral. Each points to a different root cause. If you filter byxfer-inand see nothing, broaden the search togeneral.Test TCP connectivity to the primary. Zone transfers use TCP. Run
dig +tcp +time=2 +tries=1 @primary-ip zone SOA. If this fails but UDP works, the problem is TCP-specific: firewall,tcp-clientsexhaustion, or a primary that is not listening on TCP.Check for the unreachable-primary cache. When a TCP connection to the primary fails, BIND caches the primary as unreachable. During this window, refresh attempts are skipped even if the primary becomes reachable again. A NOTIFY message from the primary clears this cache early. If you just fixed a network issue, either wait for the cache to expire or run
rndc retransfer zoneto bypass it.Verify TSIG if configured. If transfers use TSIG authentication, verify that the key name, algorithm, and secret match exactly on both primary and secondary. A common failure is rotating the key on one side but not the other. TSIG is the only supported transaction signature mechanism for zone transfers in current BIND releases.
Check for journal corruption. If transfers succeed but the secondary keeps doing full AXFR instead of IXFR, the secondary’s
.jnlfile may be corrupted. This forces a full zone transfer on every refresh, which is wasteful and can fail on large zones under transfer quotas.Check BIND version-specific changes. If transfers started failing after an upgrade:
- BIND 9.20.0 changed the
allow-transferdefault. Without an explicitallow-transferACL at the zone, view, or options level on the primary, outgoing transfers may be denied. - BIND 9.20.0 removed
alt-transfer-source,alt-transfer-source-v6, anduse-alt-transfer-source. Configurations relying on these will failnamed-checkconf. - BIND 9.18.0+ performs stricter validation of the question section in AXFR, IXFR, and SOA replies. Transfers from older or non-compliant primaries may produce FORMERR.
- BIND 9.20.0 changed the
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| SOA serial mismatch (primary vs secondary) | Earliest indicator that a transfer failed or is lagging | Any mismatch persisting beyond the refresh interval |
| SOA expire runway | The actual countdown to zone removal | Below 50% of expire value; critical below 25% or 24 hours |
| Transfer failure counters (zonestats) | Cumulative count of failed transfers since startup | Non-zero and increasing between polls |
| Transfer success counters (zonestats) | Confirms transfers are completing | Counter stops incrementing |
| xfer-in log entries | Contains the specific failure reason | Any error-level entry in xfer-in category |
| TCP connectivity to primary on port 53 | Transfers require TCP; UDP health checks hide transfer failures | TCP SOA query fails while UDP succeeds |
| Transfer quota utilization | Secondary defers transfers when limits are reached | Quota deferral messages in xfer-in logs |
Fixes
Primary unreachable or firewall blocking TCP/53
Verify the network path with dig +tcp @primary-ip zone SOA. If TCP fails, check firewall rules on the primary and any intermediate network devices. Zone transfers require TCP/53, which is often blocked by default in cloud security groups and corporate firewalls even when UDP/53 is open.
If the primary was unreachable and you fixed the issue, BIND may still have it cached as unreachable. Run rndc retransfer zone on the secondary to force an immediate transfer attempt.
TSIG key mismatch
Compare the key definitions on both servers. The key name, algorithm (typically hmac-sha256), and base64-encoded secret must match exactly. After correcting the key on one side, run rndc retransfer zone on the secondary.
Journal corruption
If the secondary repeatedly falls back from IXFR to AXFR, the .jnl file may be corrupted. To recover:
- Stop named on the secondary.
- Delete the zone’s
.jnlfile. - Start named.
- Run
rndc retransfer zoneto force a clean AXFR.
The journal will be rebuilt from the fresh transfer. This is disruptive: stopping named briefly interrupts service for all zones on that server.
BIND 9.20.0 allow-transfer default change
After upgrading the primary to BIND 9.20.0 or later, add an explicit allow-transfer ACL at the zone, view, or options level. If you previously relied on alt-transfer-source for alternate transfer source addresses, migrate to transfer-source and transfer-source-v6.
Primary overloaded
The tcp-clients limit on the primary is shared between TCP client queries and zone transfers. When exhausted, the primary rejects new TCP connections and the secondary sees connection resets. Increase tcp-clients on the primary or reduce concurrent TCP query load.
On the secondary, check transfers-in (default 10) and transfers-per-ns (default 10). When these quotas are full, transfers are deferred, not failed. Increase the limits if the secondary manages many zones from the same primary.
Forcing a transfer
rndc retransfer zone forces a fresh transfer on the secondary. On older versions, rndc retransfer queues a transfer but does not abort a stuck one, so you may need to wait for the current attempt to time out.
After a forced transfer, verify with rndc zonestatus zone that the serial now matches the primary and the expire runway has reset.
Prevention
- Monitor SOA serial consistency between primary and all secondaries. Compare serials on a schedule shorter than the refresh interval. Any mismatch is the earliest sign of a transfer problem.
- Track the SOA expire runway as a trend, not a threshold. Alert when runway drops below 50% of the expire value. Page when it drops below 25% or 24 hours, whichever is shorter.
- Capture xfer-in log entries in your monitoring pipeline. The log category contains the specific failure reason. Alert on error-level entries.
- Watch transfer failure counters. Any non-zero rate between polls means transfers are failing.
- Test TCP/53 connectivity independently of UDP. A UDP-only health check will never catch transfer-path failures.
- Validate configuration before every reload. Run
named-checkconfbeforerndc reloadto catch ACL changes and removed directives before they break transfers. - After upgrades, verify transfers explicitly. BIND version changes can silently break transfers.
How Netdata helps
- SOA serial mismatch detection correlates serials between primary and secondary, flagging divergence within minutes instead of waiting for the expire timer to surface the problem.
- SOA expire runway trending tracks the countdown as a time-series signal. Alerting at 50% and 25% thresholds gives operators hours or days of lead time before the zone goes dark.
- xfer-in log correlation alongside TCP connectivity checks to the primary identifies whether the failure is network-level, authentication-level, or capacity-level.
- Transfer counter monitoring surfaces when transfers stop completing and for how long.
- Composite alerting on failed transfers combined with a shrinking expire runway catches the specific failure pattern that causes silent outages: a zone serving stale data for days before the secondary removes it.
Related guides
- BIND cache eviction storms: DeleteLRU, an undersized max-cache-size, and the pressure spiral
- BIND cache hit ratio dropping: the leading edge of recursive pain
- BIND clients-per-query and max-clients-per-query: duplicate recursion for popular names
- BIND cold cache after restart: the warming storm and elevated upstream load
- BIND forwarding loops: recursion that never terminates and burns recursive slots
- How BIND actually works in production: a mental model for operators
- BIND lame delegations: ’lame server resolving’ and nameservers that are not authoritative
- BIND max-cache-size: sizing the resolver cache without triggering the OOM killer
- BIND monitoring checklist: the signals every production resolver and authoritative server needs
- BIND monitoring maturity model: from survival to expert
- BIND ’no more recursive clients: quota reached’: the recursive-clients circuit breaker
- named not responding on port 53: total outage versus UDP-works-TCP-fails






