named vanished from the process table. systemctl status named shows inactive (dead) or a restart timestamp from minutes ago. The likely cause: the Linux OOM killer terminated named when system memory was exhausted.
The cycle is self-reinforcing. BIND’s RSS grows steadily (excessive cache allocation, oversized RPZ, or a version-specific leak) until the kernel OOM killer selects named as the victim. All DNS resolution fails instantly. If systemd restarts the service, the cold cache forces every query through recursion, creating a warming storm that spikes upstream load. If the memory condition persists, the cycle repeats: start, grow, OOM, kill, restart.
The diagnostic trail is straightforward. dmesg | grep -i oom names named as the killed process. systemctl show named -p NRestarts shows a climbing restart counter. BIND’s RSS was climbing before the kill.
What this means
OOM termination is an instantaneous DNS outage with no warning. The kernel does not signal the application. It reclaims memory by terminating the process with the highest oom_score.
For recursive resolvers, the aftermath compounds the problem. A cold cache means every incoming query triggers outbound recursion. On a high-traffic resolver doing tens of thousands of queries per second, the cold-start outbound rate equals the inbound rate, far above the normal 5-20% ratio a warm cache provides. This warming storm means elevated upstream load, increased recursive client utilization, and higher latency for all clients during the 30-60 minutes it takes the cache to reach operating temperature.
If the memory pressure is structural (not a transient spike), systemd’s auto-restart simply restarts named into the same environment. The cycle repeats with a period determined by how long it takes RSS to climb back to the OOM threshold.
flowchart TD
A["RSS climbs steadily"] --> B{"OOM killer triggers?"}
B -->|Yes| C["named terminated
Total DNS outage"]
C --> D{"systemd auto-restart?"}
D -->|Yes| E["named restarts
with cold cache"]
E --> F["Warming storm:
elevated upstream load"]
F --> G{"Memory pressure
persists?"}
G -->|Yes| A
G -->|No| H["Cache warms
over 30-60 min"]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| max-cache-size not configured | RSS grows toward physical memory limit on a shared server. Default is 90% of RAM for recursive views. | named-checkconf -p | grep -i max-cache-size |
| Oversized RPZ datasets | RSS jumps after RPZ zone load or transfer. Large blocklists consume memory outside cache accounting. | Check RPZ zone count and dataset sizes. |
| Version-specific memory leak | RSS grows monotonically without stabilizing, even after the cache warmup plateau. | Compare BIND version against ISC security advisories. |
| Large authoritative zones | RSS dominated by zone data in memory, not cache. Zone data is not subject to max-cache-size. | Compare total zone data size to cache size. |
| Chroot with percentage max-cache-size | max-cache-size as a percentage resolves to effectively unlimited when /proc is not visible inside the chroot. | Set an explicit byte or MB value instead. |
Quick checks
Run these read-only commands to confirm the diagnosis. On Debian/Ubuntu, the service is bind9, not named.
# Confirm daemon is down
pgrep -x named || echo "named NOT RUNNING"
# Confirm OOM kill in kernel log (use journalctl -k if dmesg requires privileges)
dmesg -T | grep -i oom | tail -20
# Check systemd restart count
systemctl show named -p NRestarts
# Current RSS of named process (KB) - only if running
pgrep -x named >/dev/null && awk '/VmRSS/{print $2, $3}' /proc/$(pgrep -x named)/status
# System memory overview
free -m
# Check max-cache-size in parsed config
named-checkconf -p 2>/dev/null | grep -i max-cache-size
# BIND internal status (if control channel is responsive)
rndc status 2>&1 | head -5
# Process memory snapshot with uptime
pgrep -x named >/dev/null && ps -o pid,rss,vsz,%mem,etime -p $(pgrep -x named)
# Check BIND version and build options
named -V 2>/dev/null | head -5
How to diagnose it
Confirm the OOM kill. Run
dmesg -T | grep -i oom. Look for lines namingnamedas the killed process. The kernel logs the oom_score, RSS, and total memory state at kill time. Ifnamedis not in the OOM output, the process died for a different reason (crash, signal, assertion failure) and the rest of this article does not apply.Check the restart history. Run
systemctl show named -p NRestarts. A climbing counter over a short window confirms a restart storm. Cross-reference withjournalctl -u named --since "1 hour ago"to see restart timestamps and any BIND messages before each termination.Examine the memory configuration. Run
named-checkconf -p | grep -i max-cache-size. If nothing is returned, max-cache-size is not explicitly set and BIND applies the default: 90% of physical memory for recursive views. On a server with 16 GB RAM, the cache alone can grow to 14.4 GB before BIND’s internal accounting intervenes, leaving almost nothing for the OS, zone data, RPZ, or other processes.
Determine what is consuming memory. Check BIND’s internal memory accounting via the statistics channel (HTTP JSON endpoint) or
rndc statsoutput. Look at cache memory counters (TreeMemInUse,HeapMemInUsein cachestats) to see how much the cache itself is using versus total RSS. If RSS is significantly larger than cache plus zone data, suspect fragmentation overhead or a leak. Plan for total RSS of roughly 1.5x the configured cache size due to allocator fragmentation.Check for version-specific issues. Compare your BIND version against ISC advisories. Known memory-exhaustion vulnerabilities include GSS-API TKEY leaks (CVE-2026-3039, affects versions through 9.20.22, fixed in 9.18.49 / 9.20.23 / 9.21.22) and DNSSEC proof-of-nonexistence leaks (CVE-2026-3104, fixed in 9.20.21). If running BIND 9.19.16 or later, also verify whether
max-cache-sizeenforcement is functioning correctly for your specific patch level.
- Assess the warming storm. After a restart, check cache hit ratio and outbound query rate via the statistics channel. A 0% hit rate and outbound rate approaching inbound rate confirm the cold-cache warming storm. Monitor how long it takes for the hit ratio to recover to baseline (typically 30-60 minutes depending on traffic diversity). See the related guide on cold cache warming for detailed warming-storm analysis.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| named RSS | Direct indicator of memory pressure. | Steady growth past 70% of system RAM without stabilization. |
| System available memory | Shows headroom before OOM killer activates. | Declining trend with no corresponding cache warmup plateau. |
| NRestarts (systemd) | Detects restart loops indicating recurring OOM. | Counter incrementing multiple times within an hour. |
| Cache hit ratio | Detects cold-cache aftermath of each restart. | Drops to 0% after restart; slow recovery indicates heavy warming load. |
| Outbound query rate | Quantifies warming storm impact on upstream. | Approaches inbound rate after restart (normal warm ratio is 5-20%). |
| DeleteLRU counter | Cache under memory pressure, evicting entries before TTL expiry. | Rapidly increasing evictions correlate with undersized cache. |
| dmesg OOM events | Confirms kernel OOM killer as cause. | named named in OOM killer output with RSS and oom_score. |
Fixes
Set an explicit max-cache-size
The primary fix. The default (90% of physical RAM for recursive views) is appropriate only for dedicated DNS servers where BIND is the sole major consumer. On shared or multi-purpose servers, set an explicit limit.
In named.conf, inside the options or view block:
options {
max-cache-size 2G;
};
Guidelines:
- On dedicated DNS servers, 50% of system RAM is a reasonable starting point.
- On multi-purpose servers, allocate based on what remains after other processes. A fixed value (e.g.,
2G) is clearer than a percentage. - BIND also uses memory for zone data, in-flight query state, RPZ, DNSSEC key material, OpenSSL, libuv, and other internal contexts. These are not constrained by max-cache-size. Plan for total RSS of roughly 1.5x the cache limit.
- If running in a chroot, do not use percentage values. Set an explicit byte or MB value, because
sysconf(_SC_PHYS_PAGES)may fail without/procvisible inside the chroot, causing the percentage to resolve to an effectively unlimited value.
After changing, validate and apply without dropping the cache:
named-checkconf && rndc reconfig
rndc reconfig reloads configuration including max-cache-size. Entries exceeding the new limit are evicted gradually; the cache is not flushed. If the change does not take effect (older BIND versions may require a restart), use systemctl restart named and accept the cold-cache penalty.
Cap RPZ memory usage
RPZ datasets with millions of entries consume significant memory outside cache accounting. Factor RPZ memory into capacity planning alongside the cache. If RPZ is the dominant consumer, either increase the server’s memory budget or reduce the RPZ dataset size. Large RPZ datasets also add per-query processing overhead, compounding CPU pressure during traffic spikes.
Address version-specific leaks
If RSS grows monotonically without ever stabilizing (even after the cache warmup plateau), suspect a version-specific leak. Check ISC security advisories for memory-related CVEs. Update to a patched release. Known issues:
- CVE-2026-3039: GSS-API TKEY negotiation leaks security contexts inside the GSS library, bypassing BIND’s memory accounting. Fixed in 9.18.49, 9.20.23, 9.21.22.
- CVE-2026-3104: DNSSEC proof-of-nonexistence memory leak when more than
max-records-per-typeRRSIGs exist for NSEC records. Fixed in 9.20.21.
Enforce external memory limits
As a supplementary control, use systemd cgroup limits to provide a hard ceiling. Create or edit the unit override:
systemctl edit named
Add:
[Service]
MemoryMax=4G
MemoryHigh=3G
MemoryHigh applies backpressure (kernel throttling) as the cgroup approaches the limit. MemoryMax is the hard ceiling: if exceeded, the cgroup OOM killer terminates processes within it. This gives named a controlled failure boundary rather than letting it consume all system memory and triggering a system-wide OOM event that may kill other critical processes.
Break the restart storm
If systemd is restarting named into the same memory-starved environment, the cycle repeats. Two adjustments help:
Set a reasonable RestartSec. A short restart delay (e.g.,
RestartSec=10s) gives the kernel time to reclaim memory from the killed process before the new instance starts.Consider OOMPolicy=continue. By default, systemd may mark the unit as failed when a process is OOM-killed.
OOMPolicy=continuelets systemd restart the service normally without treating the OOM kill as a permanent failure state.
Prevention
- Set an explicit max-cache-size on every recursive resolver. The 90% default is dangerous on any server that is not dedicated to DNS. Use a fixed value or a conservative percentage.
- Monitor named RSS as a trend. Normal behavior is a warmup ramp (30-60 minutes) followed by a stable plateau. Monotonic growth without plateau indicates a leak or undersized limits.
- Keep named RSS under 70% of system memory. The remaining 30% covers OS page cache, other processes, and burst absorption.
- Keep BIND updated. Memory-related CVEs are fixed in patch releases. Running an EOL branch means accumulating known memory bugs.
- Use jemalloc. BIND 9.18+ uses jemalloc by default when available. jemalloc reduces fragmentation overhead compared to the internal allocator or glibc malloc. If compiling from source, ensure jemalloc is linked.
How Netdata helps
- Per-second RSS tracking for the
namedprocess surfaces monotonic growth before it reaches the OOM threshold, giving operators time to intervene. - System memory correlation shows available memory and named RSS on the same timeline, making it visible when
namedis consuming the majority of system RAM. - OOM event detection from kernel logs appears as discrete events on the timeline, so you can correlate the kill with the RSS trend that preceded it and the restart that followed.
- Cache hit ratio and outbound query rate tracking reveals the cold-restart aftermath. A sudden drop to 0% hit rate with a corresponding spike in outbound queries confirms the warming storm.
- Systemd restart count monitoring detects restart loops within minutes, rather than discovering them during the next user-facing outage report.
Related guides
- BIND DNSSEC validation failing: ‘broken trust chain’, ValFail, and SERVFAIL for signed domains
- 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 DNSSEC failing from clock drift: NTP, RRSIG inception/expiry windows, and SERVFAIL
- BIND dnssec-validation disabled: the security regression that ‘fixes’ SERVFAIL
- BIND dynamic update failures: UpdateFail, denied updates, and TSIG drift
- BIND forwarding loops: recursion that never terminates and burns recursive slots
- How BIND actually works in production: a mental model for operators
- BIND inline signing silently failed: missing keys and a zone served unsigned
- BIND journal (.jnl) corruption: dynamic-update and IXFR failures that block zone load






