Tuning Unbound DNS for High-Throughput Linux Workloads
Published: 18 Sep, 2026

Tuning Unbound DNS for High-Throughput Linux Workloads

Running Unbound as a high-volume local caching resolver or recursive gateway under heavy traffic quickly exposes kernel network stack bottlenecks, thread contention, and cache eviction overhead. When handling tens of thousands of queries per second (QPS), default out-of-the-box configurations suffer from dropped UDP packets, elevated query response latencies, and high context switching.

Kernel-Level UDP Buffer and Network Tuning

Before modifying Unbound configuration parameters, the Linux network subsystem must be tuned to prevent packet drops at the network socket layer. Under bursty traffic, small socket receive buffers cause immediate dropped frames at the UDP interface.

Apply these sysctl settings in /etc/sysctl.d/99-dns-performance.conf to expand memory windows and queue depths:

net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.core.rmem_default = 33554432
net.core.wmem_default = 33554432
net.core.netdev_max_backlog = 10000
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384

Reload the kernel parameters immediately with:

sudo sysctl -p /etc/sysctl.d/99-dns-performance.conf

Threading, Memory Slabs, and Socket Distribution

Unbound relies on a multi-threaded architecture where lock contention can degrade throughput if slab counters are not aligned with CPU core topology. Slabs must be set to a power of 2 closest to the number of configured worker threads.

  • num-threads: Set this to match physical core count (avoid SMT/hyperthreads for deterministic latency).
  • so-reuseport: Setting this to yes enables the Linux SO_REUSEPORT socket option, distributing incoming UDP packets directly within the kernel across separate file descriptors for each worker thread, eliminating single-socket lock contention.
  • *-slabs: Set msg-cache-slabs, rrset-cache-slabs, infra-cache-slabs, and key-cache-slabs to identical powers of 2 (e.g., 8 slabs for 8 threads, 16 slabs for 16 threads).

Cache Allocation and Aggressive Resource Prefetching

Unbound separates message headers from Resource Record Sets (RRsets). The standard sizing ratio requires allocating twice as much memory to the rrset-cache-size as to the msg-cache-size.

To prevent cache miss latency on expiring popular domains, enable proactive resolution via prefetch and prefetch-key. Furthermore, enabling RFC 8198 aggressive negative caching (aggressive-nsec) allows Unbound to synthesize NXDOMAIN responses directly from NSEC/NSEC3 records without querying authoritative nameservers.

DirectiveRecommended SettingImpact
msg-cache-size512m to 2gStores parsed DNS headers and metadata
rrset-cache-size1g to 4gStores actual DNS answer payloads (2x msg-cache)
so-rcvbuf8m / 16mAssigns explicit socket buffer memory to Unbound
prefetchyesRefreshes queries near TTL expiration automatically
prefetch-keyyesPrefetches DNSKEYs ahead of expiration for DNSSEC validation
aggressive-nsecyesReduces upstream traffic for nonexistent subdomains

Optimized Production Configuration Block

Below is a hardened, production-ready server block configured for an 8-core, 16GB RAM dedicated resolver instance located in /etc/unbound/unbound.conf.d/performance.conf:

server:
    verbosity: 1
    interface: 0.0.0.0
    interface: ::0
    port: 53
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes

    # Performance and Concurrency
    num-threads: 8
    so-reuseport: yes
    so-rcvbuf: 16m
    so-sndbuf: 16m
    outgoing-range: 8192
    num-queries-per-thread: 4096
    msg-cache-slabs: 8
    rrset-cache-slabs: 8
    infra-cache-slabs: 8
    key-cache-slabs: 8

    # Memory Allocations (Ratio: rrset = 2 * msg)
    msg-cache-size: 1024m
    rrset-cache-size: 2048m
    key-cache-size: 128m
    neg-cache-size: 64m

    # Cache Tuning & Prefetching
    cache-min-ttl: 60
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 86400
    serve-expired-client-timeout: 1800
    aggressive-nsec: yes
    infra-host-ttl: 900

    # Hardening & Security
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: no

Validating Performance and Monitoring Packet Loss

Check for kernel-level UDP buffer overflows using netstat:

netstat -su | grep -E 'receive errors|buffer errors'

If RcvbufErrors increments under heavy load, expand net.core.rmem_max and verify that so-rcvbuf in Unbound matches or exceeds the kernel window.

Extract live runtime cache hits, misses, and recursion times through unbound-control:

sudo unbound-control stats_noreset | grep -E 'total.num.queries|cache.hit|cache.miss|time.avg'

Ensuring that total.cache.hit stays above 85% with an average recursion latency under 15ms confirms the prefetch and memory slab pipeline is balanced for production workloads.