Most BIND deployments catch complete outages but miss the slow-burn failures that cause real incidents: cache pressure spirals, recursive client exhaustion, DNSSEC signature expiry, and kernel-level UDP drops that BIND itself never sees.
This article maps four monitoring maturity levels, from Survival to Expert. Each level adds signals that catch failure patterns invisible to the previous one. Use this as an inventory checklist: identify your current level, then decide which signals to add next based on whether you run a recursive resolver, an authoritative-only server, or a mixed-role deployment.
Signal names come from BIND’s statistics channel (JSON at /json/v1/*, XML at /xml/v3/*), OS-level sources like /proc/net/snmp, and rndc commands. Counter names are stable across BIND 9.16+.
flowchart TD
L1["L1 Survival
named alive, canary, RSS"]
L2["L2 Operational
rate, SERVFAIL, cache, resources"]
L3["L3 Mature
RTT, kernel drops, leading indicators"]
L4["L4 Expert
per-thread, signing, entropy, NTP"]
L1 --> L2
L2 --> L3
L3 --> L4Level 1: survival
Level 1 answers one question: is named running and answering queries? You will miss every degradation condition, but you will know about total outages.
| Signal | What it catches | Source |
|---|---|---|
| Process liveness | named crashed or killed | pgrep -x named |
| UDP canary query | Listener not responding on primary transport | dig +time=2 +tries=1 @127.0.0.1 <domain> A (recursive) or dig +norecurse @127.0.0.1 <zone> SOA (authoritative) |
| TCP canary query | Listener not responding on TCP (large responses, zone transfers at risk) | Same query with +tcp flag |
| Process RSS | named consuming all system memory | /proc/$(pgrep -x named)/status VmRSS field |
Use pgrep -x named (exact match), not -f, to avoid false positives from named-checkzone or named-checkconf. The canary query must be role-correct: a recursive resolver should probe a domain requiring recursion; an authoritative server should probe +norecurse against a locally served zone. A REFUSED response is not a dead service. It means the ACL denied the query, which may be correct behavior.
What Level 1 cannot detect: rising latency, declining cache hit ratio, increasing SERVFAIL, approaching resource limits, kernel packet drops, zone transfer failures, or DNSSEC validation problems. A BIND server can be slowly dying for days while passing every Level 1 check.
Level 2: operational
Level 2 answers: is BIND working correctly under production load, and are resources healthy?
| Signal | What it catches | Source |
|---|---|---|
| Incoming query rate | Traffic anomalies, DDoS, flash crowds | Requestv4, Requestv6 in NSStats |
| SERVFAIL rate | Resolution failures (upstream timeout, DNSSEC, broken delegation, recursive limit) | QrySERVFAIL in NSStats, as ratio of classified responses |
| Recursive clients | BIND’s circuit breaker approaching trip point | RecursClients in NSStats, as percentage of recursive-clients limit (default 1000) |
| Cache hit ratio | Cache effectiveness declining, leading to upstream load | CacheHits / (CacheHits + CacheMisses) per view |
| Protocol distribution | TCP share elevation (truncation, transfers, attacks) | QryUDP, QryTCP in NSStats |
| Query rejections | ACL misconfiguration or unauthorized access | AuthQryRej, RecQryRej in NSStats |
| CPU utilization | Processing bottleneck | Process-level CPU for named |
| File descriptor usage | FD exhaustion causing silent query drops | /proc/$(pgrep -x named)/fd count vs limit |
| SOA serial consistency | Zone transfer failure between primary and secondary | External SOA query comparison |
| DNSSEC validation failures | Clock drift, expired trust anchors, upstream signing issues | ValFail per-view resolver stat |
| Zone load health | Zone failed to load after reload or restart | BIND logs, rndc zonestatus |
The most important transition from Level 1 to Level 2 is monitoring RecursClients as a percentage of the configured limit. The recursive-clients option (default 1000) is BIND’s circuit breaker. A soft quota engages around 90% (900), and at the hard limit (1000), new recursive queries receive SERVFAIL. This is how a single slow upstream nameserver cascades into a local resolver outage: the count climbs from stressed to broken with no graceful degradation in between.
Express SERVFAIL rate as a ratio: QrySERVFAIL divided by the sum of classified responses (QrySuccess + QrySERVFAIL + QryNXDOMAIN + QryFORMERR + QryNxrrset + QryReferral). Normal is near 0%. Above 0.1% warrants investigation. Above 1% indicates a systemic problem. SERVFAIL is cached (negative caching), so a momentary upstream outage causes sustained SERVFAIL for the negative TTL duration. Fixing upstream does not instantly fix the metric.
named starts successfully even if individual zones fail to load. Process liveness checks do not cover this. After every rndc reload, verify zone count via rndc status and scan logs for load failures.
File descriptor limits are OS-controlled. The BIND files option is deprecated in 9.18 and removed in 9.20. Default ulimit -n is often 1024, which is dangerously low for a busy resolver. BIND logs “too many open files” and silently drops queries when the limit is hit.
Level 3: mature
Level 3 answers: is BIND degrading, and are there invisible failures the previous levels missed? This is where leading indicators and kernel-level signals enter.
| Signal | What it catches | Source |
|---|---|---|
| Resolver RTT distribution | Upstream nameserver latency increasing | QryRTT10, QryRTT100, QryRTT500, QryRTT800, QryRTT1600, QryRTT1600+ per view |
| Resolver failure counters | Timeouts, lame delegations, socket failures | QueryTimeout, Lame, Retry per-view resolver stats |
| NumFetch | Per-view active outbound fetch pressure | NumFetch per-view resolver stat |
| Response code breakdown | Granular error pattern (NXDOMAIN spike, REFUSED shift) | rcodes section: NOERROR, NXDOMAIN, SERVFAIL, REFUSED individually |
| UDP RcvbufErrors | Kernel dropping packets before BIND sees them | /proc/net/snmp Udp column |
| TCP connection count | TCP accumulation causing FD pressure | ss -tan filtered to port 53 |
| RRL activity | Rate limiting throttling legitimate traffic or blocking attack | RateDropped, RateSlipped in NSStats |
| RPZ rewrites | Threat interception spike (malware outbreak) | RPZRewrites in NSStats |
| Update activity | Dynamic update failures or unauthorized attempts | UpdateDone, UpdateFail in NSStats |
| Socket statistics | Socket-level patterns for FD diagnosis | SockStats counters |
| OpCode and QType distribution | Amplification attacks (ANY), tunneling (TXT), reconnaissance | opcodes, qtypes sections |
| SOA expire countdown | Secondary zone approaching expiry cliff | rndc zonestatus <zone> |
| Cache eviction counters | Cache under memory pressure, undersized | DeleteLRU, DeleteTTL in cachestats |
| Control-plane responsiveness | rndc degraded, incident response impaired | rndc status execution timing |
The most important addition at Level 3 is UdpRcvbufErrors from /proc/net/snmp. BIND’s statistics only count queries it successfully read from the socket. Packets dropped by the kernel due to receive buffer overflow produce no log entry, no counter, nothing in BIND’s own accounting. Teams typically discover this during an incident where “BIND is fine but queries are disappearing.” The metric is invisible to BIND and must be collected from the OS.
The second key addition is the SOA expire countdown. Serial mismatch tells you a transfer failed, but the real danger is how much time remains before the secondary stops serving the zone entirely. Once expired, the secondary returns SERVFAIL or REFUSED for that zone. rndc zonestatus <zone> shows the expire time directly. Alert when the runway drops below 50% of the SOA expire value. Escalate at 25% or 24 hours, whichever is shorter. This is a ticking bomb, not a self-healing condition.
Resolver RTT buckets measure outbound recursive query response times to upstream nameservers, not end-to-end client-perceived latency. BIND has no native inbound latency histogram. A shift toward higher RTT buckets indicates upstream degradation, which drives cache-miss latency and recursive client accumulation. These are per-view counters, so in split-horizon deployments, check each view separately.
Cache eviction counters (DeleteLRU, DeleteTTL) reveal whether the cache is at capacity and evicting entries before TTL expiry. Rapid DeleteLRU increase correlates with undersized max-cache-size and declining hit ratio.
Level 4: expert
Level 4 answers: is the resolver secure, correlated across system layers, and ahead of every known failure mode? These are the signals experienced operators add after major incidents.
| Signal | What it catches | Source |
|---|---|---|
| Per-thread CPU utilization | Single-thread bottleneck while aggregate CPU looks moderate | pidstat -t -p $(pgrep -x named) |
| rndc recursing sampling | Which upstream nameservers are causing recursive pile-up | rndc recursing output analysis |
| Cache memory tracking | Cache consuming memory, approaching max-cache-size | TreeMemInUse, HeapMemInUse, CacheNodes in cachestats |
| RRSIG expiry | Authoritative signatures approaching expiry (silent signing failure) | dig <zone> RRSIG +dnssec +multiline, rndc signing -list <zone> |
| Query name entropy | Random subdomain attack (water torture) | Query name distribution analysis from logs or sampling |
| NTP clock offset | Clock drift causing DNSSEC validation failures | timedatectl status, chronyc tracking |
| Per-core CPU and IRQ/softirq | IRQ imbalance causing packet processing bottleneck | mpstat -P ALL 1 5 |
| Source port entropy | Weak randomization, cache poisoning vulnerability | dig +short porttest.dns-oarc.net TXT @127.0.0.1 |
| Statistics channel response time | BIND under severe internal pressure | Time the statistics channel HTTP response |
| Zone file integrity checksums | Unauthorized zone modifications | Checksum comparison after each reload |
| DNSSEC signing freshness | Inline signing silently failed (missing .signed.jnl) | File presence check for .signed.jnl |
Per-thread CPU matters because BIND 9.16+ uses one worker thread per CPU core by default (-n flag to override). Aggregate CPU can look moderate while a single thread is saturated, creating a bottleneck invisible in process-level metrics. Use pidstat -t or thread-level views to see per-thread distribution. Per-core CPU saturation and IRQ/softirq imbalance compound this: if network interrupts are not balanced across cores, one core handles all packet processing while others sit idle.
RRSIG expiry monitoring is essential for authoritative zones with inline signing. Auto-signing can silently fail when key files are missing, permissions are wrong, or disk is full. No log error is produced, no .signed.jnl file is created, and the zone continues being served with expiring signatures because the authoritative server does not validate its own signatures. Validating resolvers worldwide reject the zone when signatures expire, producing hours-long outages invisible from the authoritative operator’s perspective. The only reliable detection is checking actual RRSIG validity windows and .signed.jnl file presence.
NTP offset tracking matters because DNSSEC depends on accurate time. Even 5 minutes of clock drift can cause validation failures. Correlating NTP offset with ValFail counters catches this before it manifests as broad SERVFAIL for signed domains.
Source port entropy is the last line of defense against cache poisoning. BIND randomizes source ports for outbound recursive queries. NAT devices, firewalls, or misconfiguration can constrain this entropy. The porttest.dns-oarc.net probe reports the quality of randomization. Anything below “GREAT” warrants investigation.
rndc recursing sampling reveals which upstream nameservers are causing pile-up during recursive resolution cascades. During incidents, this distinguishes a single bad upstream from a broad network issue. It is invaluable during incidents but almost never collected proactively.
Cross-level failure patterns
Three composite failure patterns span multiple levels and require cross-level signal correlation to diagnose correctly:
- Recursive resolution cascade:
RecursClientsclimbing toward limit (Level 2) plusQueryTimeoutincreasing per view (Level 3) plusrndc recursingshowing pile-up on specific upstreams (Level 4). CPU may remain moderate because threads are blocked waiting, not computing. - DNSSEC time bomb:
ValFailspiking (Level 2) plus SERVFAIL for signed domains while unsigned domains work normally (Level 3) plus NTP offset drift (Level 4). The+cdflag indigbypasses validation and confirms DNSSEC as the cause. - Zone staleness cascade: SOA serial mismatch between primary and secondary (Level 2) plus expire countdown shrinking (Level 3) plus DNSSEC signing freshness check for the zone (Level 4).
How Netdata helps
Netdata’s BIND collector ingests statistics channel data at per-second granularity, which matters for several reasons specific to this maturity model:
- Rate computation is automatic. BIND’s counters are cumulative since process start. Netdata computes deltas and rates, so you see queries-per-second and SERVFAIL-per-second without manual sampling intervals.
- Kernel metrics sit alongside BIND metrics.
UdpRcvbufErrorsfrom/proc/net/snmpappears on the same timeline as BIND’s query counters, making the Level 3 kernel-drop gap immediately visible without a separate dashboard. - Per-view breakdowns surface cache hit ratio, resolver failure counters (
QueryTimeout,Lame), and RTT distributions in context, supporting split-horizon debugging. - Process-level metrics (RSS, FD count, per-thread CPU) are collected natively alongside the BIND collector, so Level 4 signals do not require separate tooling or manual correlation.






