unbound as a Recursive Resolver
Introduction¶
A recursive resolver answers "what is the A record for example.com" for clients by walking the DNS hierarchy (or forwarding to another resolver). unbound is the go-to: small, audited, validating, and fast. Use it as a LAN resolver, a per-host cache, or the recursive half behind an authoritative BIND.
A LAN resolver config¶
# /etc/unbound/unbound.conf
server:
interface: 10.0.0.53
interface: 127.0.0.1
port: 53
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
access-control: 0.0.0.0/0 refuse # default-deny for the world
do-ip6: yes
prefer-ip6: no
hide-identity: yes
hide-version: yes
# DNSSEC validation
auto-trust-anchor-file: "/var/lib/unbound/root.key"
val-clean-additional: yes
# cache / performance
cache-min-ttl: 0
cache-max-ttl: 86400
prefetch: yes # refresh popular entries before they expire
prefetch-key: yes
msg-cache-size: 128m
rrset-cache-size: 256m
num-threads: 4
so-reuseport: yes
# privacy / hardening
qname-minimisation: yes
aggressive-nsec: yes
harden-glue: yes
harden-dnssec-stripped: yes
unwanted-reply-threshold: 10000000
# local overrides
local-zone: "corp.internal." static
local-data: "gitlab.corp.internal. IN A 10.0.5.10"
local-data: "10.0.5.10 IN PTR gitlab.corp.internal."
# split-horizon: forward an internal zone to the internal authoritative server
forward-zone:
name: "corp.internal."
forward-addr: 10.0.5.2
remote-control:
control-enable: yes
Full recursion vs forwarding¶
- Full recursion (default, no
forward-zone: name: ".") — unbound queries the roots and follows delegations itself. Maximum privacy (no third party sees all your queries), resilient. Slightly slower cold cache. - Forwarding — send everything to an upstream (1.1.1.1, 9.9.9.9), optionally over DoT:
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 9.9.9.9@853#dns.quad9.net
Use forwarding when full recursion is blocked (some networks filter outbound 53 to non-resolvers), or when you want the upstream's filtering/malware blocking. Otherwise full recursion is the better default.
Local zones and RPZ¶
local-zone/local-data— hardcode records, block domains (local-zone: "ads.example." always_nxdomain), or override.- RPZ (Response Policy Zones) — load a blocklist as a zone
(
rpz:clause) for ad/malware blocking at scale, updatable via zone transfer.
Verification and troubleshooting¶
unbound-checkconf
unbound-control status
unbound-control stats_noreset | grep -E 'total.num.queries|hit|recursion.time'
unbound-control reload
unbound-control flush_zone example.com
unbound-control dump_cache | head
dig @10.0.0.53 example.com +dnssec # ad flag = validated
dig @10.0.0.53 dnssec-failed.org # should SERVFAIL (validation working)
SERVFAILfor everything — DNSSEC anchor missing/stale (unbound-anchor -a /var/lib/unbound/root.key), no outbound 53/853, or full recursion blocked (switch to forwarding).unbound-control statsnum.query.tcp/unwanted.REFUSED— the client's subnet isn't inaccess-control: ... allow.- Internal names not resolving — no
local-data/forward-zonefor the internal zone, orqname-minimisationinteracting badly with a broken internal auth server (rare — tryqname-minimisation-strict: no). - Stale record after a DNS change — TTL still in cache;
unbound-control flush <name>or lowercache-max-ttl.prefetchrefreshes popular ones but respects TTL. - Slow first lookups — full recursion cold cache;
prefetch: yesand a warm cache fix steady state. Or forward for lower cold latency. dnssec-failed.orgresolves (should fail) — validation isn't actually on: missing/invalid trust anchor, orval-permissive-mode: yesset. Fix the anchor.- High memory — cache sizes;
msg-cache-size+rrset-cache-sizeare the big ones. Size to your query diversity. - On a host with systemd-resolved — port 53 conflict. Disable
systemd-resolved's stub (
DNSStubListener=no) or bind unbound to a different address.
Related tools and reading¶
- On-site: DNS lookup, DNSSEC checker.
- Related posts: Running BIND 9 as an authoritative server, DNS over TLS and DNS over HTTPS.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Web & Mail Servers
Running BIND 9 as an Authoritative Server
BIND as an authoritative server: zone files, secured AXFR with TSIG, SOA serial rules, and DNSSEC inline-signing. Here's a working master/slave setup.
October 24, 2026Varnish Cache in Front of a Site
Varnish is a caching HTTP accelerator configured in VCL. Here's the request lifecycle, why you strip cookies, how bans work, and grace mode for serving stale during backend trouble.
October 24, 2026Let's Encrypt with Certbot, Including Renewal That Doesn't Break
Getting the first cert is easy; keeping renewal working for years is the part people get wrong. Webroot vs DNS-01, deploy hooks, and the failure modes.
October 24, 2026