Ramblings of an aging IT geek
← Ramblings of an aging IT geek
networking

Running My Own Recursive Resolver

Swapping the upstream DNS forwarder for a real recursive resolver in the homelab, and what changed when I stopped asking someone else's server every question.

Network cables coiled in a rack

For years my homelab DNS was a forwarder. Pi-hole on the front for blocking, pointed upstream at a public resolver, and that was the extent of my thinking about it. Every lookup that missed the cache went out to someone else's server, which answered, and I never gave it another thought. It worked. It also meant a third party saw every domain every device on my network ever asked about, which I'd half-noticed and decided not to mind.

I finally swapped the forwarder for a real recursive resolver: Unbound, doing the recursion itself rather than handing it off. The difference is in what the resolver does when it doesn't know an answer. A forwarder asks another resolver. A recursive resolver starts at the root servers and walks the chain itself, root to TLD to authoritative, building the answer from the source.

The config is smaller than I expected. The core of it:

server:
    interface: 0.0.0.0
    access-control: 10.0.0.0/8 allow
    cache-min-ttl: 300
    prefetch: yes
    harden-dnssec-stripped: yes
    qname-minimisation: yes

The setup that actually matters is what's not there: no forward-zone. That single omission is the whole point. Without a forwarder, Unbound resolves everything itself, and no single upstream sees the full picture of what my network is curious about. The queries still go out, of course, the root and authoritative servers see their slice, but nobody sees all of it joined up against my address.

A datacenter aisle of equipment

Two settings earned their place quickly. qname-minimisation means Unbound only tells each server in the chain as much as it needs: it asks the root about .com, not about the full hostname I'm actually after, and only reveals the complete name to the authoritative server that genuinely needs it. It's a small privacy win that costs nothing. And prefetch quietly refreshes popular records before they expire, so the cache rarely makes me wait for the records I hit most.

I'll be honest about performance, because it's the thing people expect to be the headline and it isn't. A cold cache is slower than a forwarder. Walking from the root takes more round trips than asking one well-warmed public resolver that already knows the answer. The first lookup of a new domain has a noticeable extra beat. But the cache fills fast, and for everything I actually visit repeatedly, the answer is now local and instant, which more than evens out in daily use.

DNSSEC validation came along for free, which I appreciated more than I expected. Unbound validates signatures itself, so a tampered answer gets rejected at my resolver rather than trusted on faith from upstream. With a forwarder I was trusting the forwarder to have done that. Now I'm doing it myself, which is rather the theme of the whole exercise.

The thing I keep coming back to is how little stands between my network and the actual DNS root now. For years there was always a middleman answering my questions, perfectly competently, and I'd accepted that as just how DNS worked. It isn't. The root servers will answer me directly, the same as they answer everyone, and running the resolver that asks them is a couple of dozen lines of config and a service that's been quietly solid since the day I started it. I should have done it years ago.