A BIND secondary serving stale data is not broken. It answers queries with old but functional records, and from the outside it looks healthy. The danger is the countdown underneath: the SOA expire timer, ticking from the last successful zone transfer. When it reaches zero, the secondary stops serving the zone and returns SERVFAIL or REFUSED for every query against it.
Most teams monitor SOA serial consistency between primary and secondary. That catches “the secondary is behind” but not how much time remains before it gives up entirely. The expire runway does. It degrades over hours to days, making it ideal for early warning. It is the difference between catching a transfer failure with days of runway and discovering it after the zone has expired and clients are failing.
What it is and why it matters
Every DNS zone has an SOA record. The last four fields control secondary transfer and expiry behavior:
| Field | Meaning | Typical value |
|---|---|---|
| refresh | How often the secondary checks the primary for a new serial | 3600 to 86400 seconds |
| retry | Wait time before retrying after a failed refresh | 600 to 3600 seconds |
| expire | Maximum time the secondary serves the zone without a successful transfer | 604800 to 1209600 seconds (1 to 2 weeks) |
| minimum | Default negative TTL, also used for zone caching | 86400 seconds |
The expire value defines how long a secondary continues serving a zone without a successful transfer. Once the elapsed time since the last successful transfer exceeds expire, the secondary removes the zone from its active set and stops answering.
Serial mismatch tells you the secondary is behind. Expire runway tells you when it gives up. A secondary one hour into a seven-day expire window is behind but fine. A secondary six days into a seven-day window is one day from dropping the zone.
How it works
BIND tracks the time of the last successful zone transfer (AXFR or IXFR) and compares it against the SOA expire value:
time_remaining = SOA_expire - time_since_last_successful_transfer
Read this directly from rndc zonestatus on the secondary:
rndc zonestatus example.com
The output includes an expires: field showing the absolute time when the zone will expire if no successful transfer occurs before then.
To compute the runway manually, query the SOA to get the expire value:
# SOA fields: mname rname serial refresh retry expire minimum
dig @secondary-ip example.com SOA +short
The sixth field is expire in seconds. Note that dig gives you the configured expire value, not the time of the last transfer. To compute remaining runway you also need the last successful transfer timestamp, which comes from rndc zonestatus, BIND’s xfer-in log category, or the zone file modification time on the secondary’s filesystem.
The runway resets to full on every successful transfer. A secondary that transfers successfully every refresh interval never approaches the cliff. The runway only matters when transfers start failing.
flowchart TD
A[Successful transfer] --> B[Serving zone
runway = SOA expire]
B --> C{Refresh interval fires}
C -->|Serial changed, transfer OK| B
C -->|No serial change| B
C -->|Transfer fails| D[Retry per SOA retry interval]
D -->|Transfer succeeds| B
D -->|Transfer fails again| E{Expire runway
exhausted?}
E -->|No, runway remains| D
E -->|Yes, elapsed time
exceeds expire| F[Zone expired
secondary returns SERVFAIL]
F -->|Primary reachable again| G[Full AXFR required
IXFR not sufficient]
G --> BOnce the expire timer fires, the zone transitions to expired. Recovery requires a full AXFR; an incremental IXFR is not sufficient.
Where it shows up in production
Hidden primary with public secondaries. The primary is not in the NS records and receives no direct queries. If it goes down or becomes unreachable, every secondary starts counting down. The zone resolves for days, then all secondaries expire in sequence.
Cascading secondaries. A secondary transfers from another secondary rather than from the primary. If the upstream is stale or expired, the downstream may inherit incorrect expiration data. BIND’s request-expire option (default yes) enables the EDNS EXPIRE option on outgoing queries, letting a downstream secondary learn the upstream’s actual expiration timer. Without this, a cascading secondary may set its expire timer incorrectly.
Firewall or network changes. A rule change that blocks TCP port 53 between primary and secondary stops transfers immediately. The secondary continues serving stale data until expire. The failure is invisible because the secondary still answers queries with old data.
TSIG key rotation. If you rotate the TSIG key for zone transfers but only update one side, transfers fail silently. The secondary counts down while the primary is healthy and reachable.
Primary decommissioned without cleanup. The primary is shut down or repurposed. Secondaries keep serving from their last transfer. After expire runs out, the zone disappears from all secondaries simultaneously.
What you cannot override on the secondary
BIND provides clamping options for refresh and retry timers: min-refresh-time, max-refresh-time, min-retry-time, max-retry-time. There are no equivalent options for expire. A secondary must honor the SOA expire value from the primary.
BIND does enforce internal bounds on the expire value regardless of the SOA record:
- The minimum effective expire is
refresh + retry, with each floored at 300 seconds. A zone never expires in less than approximately 10 minutes, even if the SOA claimsexpire 0. - The maximum effective expire is 14515200 seconds (24 weeks).
You cannot extend a zone’s expire on the secondary side. If the primary publishes a dangerously short expire, the only fix is to change the SOA record on the primary.
Alerting and thresholds
The expire runway is a countdown, not a binary signal. Alert on the percentage of runway remaining, not a fixed time threshold alone.
| Runway remaining | Severity | Action |
|---|---|---|
| Above 50% | INFO | Track for trending |
| Below 50% | TICKET | Investigate transfer failure; runway is shrinking |
| Below 25% or below 24 hours (whichever is shorter) | PAGE | Zone is at risk; fix transfers now |
The “whichever is shorter” rule matters for zones with extreme expire values. A zone with a 1-hour expire at 25% has 15 minutes left. A zone with a 41-day expire at 25% has over 10 days. The 24-hour floor ensures at least one day to respond for long-expire zones; the 25% threshold catches short-expire zones early.
Cold-start gating
A newly configured secondary may show a low or unknown runway before the initial transfer completes. Gate the alert on whether the secondary has successfully served the zone before:
- Only alert on zones where the secondary has a valid serial and has completed at least one successful transfer.
- Suppress alerts for zones that have never transferred successfully. They are in initial provisioning, not failure.
This prevents paging during the window between adding a zone to the secondary’s configuration and the first successful AXFR.
What happens at expiry
When the expire timer fires, BIND logs a message in the general category following the pattern zone <zone>/IN: expired. The secondary stops answering queries for that zone, returning SERVFAIL or REFUSED. Other zones on the same server continue normally. The server itself looks healthy: most queries succeed, and only queries for the expired zone fail.
Recovery requires a full AXFR. Once the primary is reachable and the transfer succeeds, the runway resets.
# Force a full zone transfer on the secondary.
# Can be resource-intensive for large zones transferred over slow links.
rndc retransfer example.com
Use after resolving whatever prevented transfers from succeeding.
Signals to watch in production
| Signal | Why it matters | Warning sign |
|---|---|---|
SOA expire runway (from rndc zonestatus) | Direct countdown to zone expiry | Runway below 50% of SOA expire value |
| SOA serial mismatch (primary vs secondary) | Early indicator that transfers are failing | Secondary serial behind primary for longer than refresh interval |
Transfer log entries (xfer-in category) | Shows transfer attempts, failures, and success | Repeated failures with no success entries |
| Primary reachability | Root cause of most expire countdowns | dig @primary <zone> SOA timing out or returning wrong serial |
| Per-zone SERVFAIL rate | Confirms the zone has already expired | SERVFAIL for a specific zone only, not across all queries |
Expire runway is the leading indicator. Serial mismatch is the early warning. SERVFAIL for the zone confirms it has already expired. If you see SERVFAIL, you are too late; the runway should have caught it days earlier.
How Netdata helps
Netdata’s BIND collector surfaces the signals that correlate with expire runway degradation:
- Per-second query response code distribution shows SERVFAIL for a specific zone the moment it starts, before users report it.
- Zone transfer counters from the BIND statistics channel show whether transfers are succeeding or failing in near real time.
- Anomaly detection on query patterns flags the shift when a secondary starts refusing queries for an expired zone while other zones remain healthy.
- Correlating transfer failures with network-level metrics (TCP connection state, primary reachability) shortens diagnosis from “zone expired, start investigating” to “transfers failed 3 days ago, here is why.”
- Composite alerting across serial mismatch, transfer failure, and SERVFAIL matches the actual failure progression rather than a single binary alarm.
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 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
- BIND resolver NumFetch per view: per-view recursive pressure in split-horizon setups






