For years my homelab "resolver" was a forwarder. dnsmasq took every query and handed it straight to 8.8.8.8, then cached the answer for a bit. It worked, but it meant every name I looked up went to Google first. I decided to run a real recursive resolver instead, one that talks to the root servers and walks the tree itself, and answers to nobody upstream.
I went with Unbound. It's small, it's well behaved, and the config is readable, which matters when you're going to forget how it works and come back to it in six months.
what recursion actually buys you
A forwarder asks one big server "what's the address for this?" and trusts the reply. A recursive resolver does the legwork: it asks a root server who handles .pm, asks them who handles i0.pm, and so on down, building the answer from authoritative sources. The practical differences are small but real. No single third party sees every query I make. Validation happens locally. And the cache is mine, warmed by my own traffic patterns rather than shared with the internet.
The base config is barely more than this:
server:
interface: 0.0.0.0
access-control: 192.168.0.0/16 allow
access-control: 127.0.0.0/8 allow
cache-min-ttl: 60
prefetch: yes
auto-trust-anchor-file: "/var/lib/unbound/root.key"
prefetch is the nice touch: when a popular record is close to expiring, Unbound refreshes it before anyone asks again, so the common names stay hot.
the bit that bit me
The first thing that broke was DNSSEC validation, and it broke in the most confusing way: some names resolved, some returned SERVFAIL, and the set changed depending on the day. The cause was the trust anchor. Unbound validates signatures against the root key, and my root.key file had gone stale. When the chain of trust can't be built, a validating resolver doesn't shrug and return the answer anyway. It returns SERVFAIL, on purpose, because a broken signature is exactly the case validation exists to catch.
The fix was to let unbound-anchor keep the key current and make sure the file was writable by the unbound user. Once the anchor was healthy, the SERVFAILs vanished and the genuinely misconfigured domains (there are a surprising number out there with broken DNSSEC of their own) were the only ones left failing. Which is the correct behaviour, even if it's annoying when it's someone else's zone at fault.
was it worth it
For most people, no. A forwarder is simpler and faster to set up, and the privacy gain only matters if you care about it. But I do care, the cold-start latency is barely noticeable once the cache warms, and there's a quiet satisfaction in watching unbound-control stats and seeing my own little resolver walking the tree, beholden to nobody. dnsmasq still does DHCP. Unbound does the thinking. That split has turned out to be the tidiest part of the whole setup.