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

i stopped trusting someone else's resolver and ran my own

Why I moved my homelab off public DNS and onto my own recursive resolver with Unbound, what it actually bought me, and the bits that bite if you do the same.

Network cables in a patch panel

For years my homelab pointed at someone else's DNS. First the ISP's, then 1.1.1.1 when I decided I trusted Cloudflare more than my ISP, which on reflection is a strange thing to have decided. Either way, every name my machines looked up went out over the wire to a third party who could see all of it: every host I visited, every internal service I named, the lot. I wasn't paranoid about it. I just got tired of the fact that the most fundamental lookup in my network was a thing I'd outsourced and never thought about.

So I built my own recursive resolver. Not a forwarder that asks Cloudflare on my behalf, an actual resolver that talks to the root servers and walks the tree itself. This is the bit people skip, and it's the bit that matters, so let me be precise about the difference.

Forwarding versus resolving

A forwarder is a cache with delusions of grandeur. When you ask it for news.bbc.co.uk, it doesn't do any work; it asks an upstream resolver and passes the answer back to you. You've moved the trust, not removed it. Your upstream still sees every query.

A recursive resolver does the legwork. It asks a root server who handles uk, asks that server who handles co.uk, asks that one who handles bbc.co.uk, and so on down until it has an authoritative answer. No single upstream sees your full browsing pattern, because there isn't a single upstream. The roots only ever see you asking about top-level domains; the authoritative servers only see the queries for their own zone.

That's the privacy argument, and it's real but modest. The honest reason I did it was that I wanted to understand DNS properly, and nothing teaches you a protocol like running the awkward part of it yourself.

A rack of networking equipment in a small datacentre

Unbound, and not much else

I used Unbound. BIND can do this too, and does it well, but BIND is a Swiss Army knife with a chainsaw attachment and I didn't need most of it. Unbound is a recursive resolver and validating resolver first, and a small one. The config that does the real work is short:

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

Three of those lines earn their place beyond the obvious. qname-minimisation means Unbound only tells each server in the chain as much of the name as that server needs to answer. The root server asking for uk doesn't get told you wanted news.bbc.co.uk, it gets told you wanted something under uk, and that's all. It's a small thing and it's exactly the kind of small thing I'd outsourced without realising.

harden-dnssec-stripped makes Unbound refuse an answer if DNSSEC validation should have been possible and the signatures have gone missing, rather than shrugging and serving it. And prefetch quietly refreshes popular records before they expire, so the cache stays warm and you don't eat a cold-lookup latency spike on the names you use constantly.

What it actually cost me

The first thing that bit was the cold cache. A fresh recursive resolver knows nothing, so the first lookup of any given name walks the whole tree, and that's slower than a public resolver who's already cached the answer for half the planet. For the first day or two everything felt fractionally sluggish. After that the cache filled with the names I actually use, and now the common case is a local cache hit measured in single-digit milliseconds, which is faster than going out to the internet ever was.

The second thing was DNSSEC validation failures on domains whose operators had got their signing wrong. With a public resolver you never see these; they've either cached around it or they're lenient. Run your own validating resolver and you discover, occasionally, that a real site is genuinely broken and you're the only one strict enough to notice. That's correct behaviour, but it does mean that "DNS is down" sometimes turns out to be "someone else's DNSSEC is down and I'm the one being principled about it".

Servers and structured cabling in a comms room

The third was the root hints file, which Unbound ships with but which goes stale over years as root servers change addresses. It updates rarely enough that you'll forget it exists, which is the whole problem. I added a tiny monthly job to refresh root.hints from the IANA copy, because the failure mode of a stale one is the sort of thing you'd debug for an hour before remembering.

Was it worth it

Yes, but be clear about why. The privacy gain is genuine but partial: your traffic is still visible to the authoritative servers and to anyone watching your wire, since plain DNS isn't encrypted. If hiding queries from your ISP is the goal, DNS-over-TLS to a resolver you trust does more for that specific threat. What running your own recursive resolver actually buys you is independence. You aren't reliant on one company's resolver staying up, staying honest, and staying free. When 1.1.1.1 had a wobble a while back, half my friends' homelabs fell over and mine didn't notice, because mine doesn't ask anyone.

A few practical notes if you do this

Bind it to your internal interfaces only, and mean it. An open recursive resolver facing the internet is a gift to anyone running a DNS amplification attack, and they will find yours within hours of it being reachable. The access-control lines above are not optional decoration; they are the difference between a tidy homelab service and an unwitting contribution to someone else's DDoS. Check from the outside that port 53 is genuinely closed to the world before you walk away from it.

Give it somewhere to log, and actually look at the logs for the first week. Unbound will tell you which lookups are failing validation, which are timing out, and how warm the cache is running. The first time you see your own query log scroll past, you understand viscerally how much DNS your machines do without you ever asking them to: telemetry, update checks, CDNs resolving a dozen names to load one page. It's a lot, and it was all going to a stranger before.

And resist the urge to bolt on every feature at once. It's tempting to make the same box do ad-blocking, local zones for your internal hosts, conditional forwarding, the lot. All of that is doable and some of it is lovely, but each addition is another thing that can break your name resolution, and broken name resolution looks like everything being broken. Get the plain resolver solid and boring first. Add the clever bits later, one at a time, when the foundation has earned your trust.

And I understand DNS now in a way I didn't when it was a magic IP address in /etc/resolv.conf. That, more than the privacy, was the point.