If you’re running a home network, your DNS queries are the easiest trail of breadcrumbs to follow. Even if you’ve locked down your browsing with HTTPS, the names your devices look up often travel over plain old UDP port 53—visible to your ISP, your coffee shop’s Wi-Fi sniffer, or anyone between you and the resolver. Switching to an encrypted DNS protocol like DNS-over-HTTPS (DoH) fixes that, but you don’t want every device on your network trying to speak DoH directly to a public server at different times, leaving you with inconsistent filtering, no local caching, and a management headache. A local DoH proxy gives you a single, encrypted exit point for all your home DNS traffic, and dnsdist is one of the most flexible tools for the job.
This walkthrough sets up dnsdist on a small Linux box or VM to act as a forwarder: your clients send plain UDP or TCP DNS to it, and dnsdist shovels everything upstream over HTTPS. No per-device configuration beyond pointing them at a single IP. You get encryption without reinventing your whole LAN.
Why Encrypt Home DNS at All?
Most home routers hand out the ISP’s DNS servers via DHCP, and those queries are unencrypted. An ISP can see every domain you request. They might use that data for marketing, inject ads into NXDOMAIN responses, or simply comply with surveillance requests. It’s not about hiding something illicit; it’s about basic network hygiene.
Switching to a public resolver like Quad9 or Cloudflare over DoH means:
- Your queries leave your network wrapped in TLS, indistinguishable from other HTTPS traffic.
- DNS manipulation or hijacking becomes much harder.
- You get the privacy policy of a resolver you choose, not the one your ISP picks for you.
But telling every laptop, phone, and IoT gadget to talk DoH directly is messy. Some devices don’t support it. Some browsers ignore your OS settings. A local proxy that speaks DoH on behalf of your entire network gives you encryption without the fragmentation.
What Is dnsdist and Why Use It Here?
dnsdist is a high-performance DNS load balancer and proxy from the PowerDNS team. It’s typically used in front of recursive resolvers to apply rules, rate-limit, or split traffic. For home use, it’s a compact forwarding proxy that can accept plain DNS on one side and speak DoH, DoT (DNS-over-TLS), or even DNSCrypt on the other.
Key reasons to pick dnsdist over alternatives like dnscrypt-proxy or stubby:
- Fine-grained, Lua-based rules let you do things like forward specific domains to different resolvers, block ad domains upstream, or log queries for troubleshooting.
- It’s absurdly lightweight—a Raspberry Pi 3 can handle a household’s traffic without breathing hard.
- It can bind DoH itself, so your browser-supporting devices can skip the middleman if you want, but we’ll stick to using it as a plain-to-DoH proxy for simplicity.
- The configuration syntax is declarative and easy to read, even if you’ve never touched Lua.
A typical setup: dnsdist listens on your LAN IP on port 53 (or a dedicated IP you point DHCP at), and for each incoming query, it opens a DoH connection to an upstream resolver, gets the answer, and passes it back. To your clients, it looks like a normal DNS server.
Prerequisites and Environment
You’ll need a Linux machine that’s always on—a small virtual machine on your home server, a Raspberry Pi, or even an old Intel NUC. I’ll assume Debian or Ubuntu here, but dnsdist runs pretty much anywhere. The machine needs a static IP on your LAN.
Hardware requirements: a Raspbian install on a Pi Zero 2 W will handle 50+ active clients with no tuning. RAM usage will sit around 30–50 MB.
Before you start, lock down your server basics:
- Assign a static IP (e.g., 192.168.1.2).
- Run
apt update && apt upgradeto get current packages. - Disable any other DNS listener that might grab port 53, like systemd-resolved stub resolver.
sudo systemctl stop systemd-resolved && sudo systemctl disable systemd-resolvedand then edit/etc/systemd/resolved.confto set DNSStubListener=no if needed. Reboot to be safe.
You’ll also need a DoH upstream. Cloudflare (1.1.1.1), Quad9 (9.9.9.9), and Google (8.8.8.8) all offer DoH endpoints. The URL is what matters, not the IP. For Cloudflare, it’s https://cloudflare-dns.com/dns-query. Quad9’s is https://dns.quad9.net/dns-query. Pick a resolver whose privacy policy you’ve actually read.
One more thing: decide where to store backups of your config. A simple scp to your NAS, or even a git repo in your home directory, will save you when an SD card dies.
Installing dnsdist
dnsdist is in the official PowerDNS repositories, not always in the default distro repos. The easiest path is to add the PowerDNS repo for your OS version. For Debian 12 (Bookworm), for example:
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.powerdns.com/FD380FBB-pub.asc | sudo gpg --dearmor -o /etc/apt/keyrings/pdns.gpg
echo "deb [signed-by=/etc/apt/keyrings/pdns.gpg] http://repo.powerdns.com/debian bookworm-dnsdist-19 main" | sudo tee /etc/apt/sources.list.d/pdns.list
sudo apt update
sudo apt install dnsdist
Adjust the distro name and version (bookworm-dnsdist-19) to match what’s current—check repo.powerdns.com for the right string. If you’re on a Raspberry Pi, the same APT repo applies; dnsdist is architecture-agnostic.
After installation, the service will likely fail to start because there’s no config yet. That’s expected. Stop it with sudo systemctl stop dnsdist while we create the config file.
Configuring dnsdist for DoH Proxying
The configuration lives in /etc/dnsdist/dnsdist.conf. The file uses Lua. Start with a backup:
sudo cp /etc/dnsdist/dnsdist.conf /etc/dnsdist/dnsdist.conf.bak
Here’s a minimal, working config that listens on all interfaces on port 53 and sends everything to Cloudflare DoH:
-- Basic dnsdist configuration for home DoH proxy
-- Listen on the LAN IP for plain DNS
addLocal("192.168.1.2:53")
-- Allow queries from your local subnets
addACL("192.168.1.0/24")
addACL("127.0.0.0/8")
-- Create a DoH backend to Cloudflare
newServer({url="https://cloudflare-dns.com/dns-query", pool="doh"})
-- Rule: send all queries to the DoH pool
addAction(AllRule(), PoolAction("doh"))
-- Control socket so we can interact with dnsdist live
controlSocket("127.0.0.1:5199")
-- Basic performance tuning
setMaxTCPClientThreads(10)
setMaxUDPOutstanding(65535)
Let’s break that down:
addLocal("192.168.1.2:53")tells dnsdist which IP and port to bind. Replace with your server’s LAN IP. If you want it to listen on all interfaces, use0.0.0.0:53, but that’s less precise. For a dedicated DNS box, binding to the specific IP is cleaner.addACLlines restrict who can send queries. Without them, dnsdist defaults to RFC1918 subnets, but being explicit is safer. Add your guest VLAN, IoT VLAN, etc., if you have separate subnets.newServerdefines the DoH backend. Theurlparameter is a full HTTPS endpoint. We give it a pool name so we can manage routing later. You can add multiple servers: one for Cloudflare, one for Quad9, with different pools, and create rules that split traffic based on domain suffix. For now, a single pool gets us encryption.addAction(AllRule(), PoolAction("doh"))sends every query to the “doh” pool. This is the simplest rule set; you can later switch to blocking rules or domain-based steering.controlSocketopens a console. Connect withdnsdist -c 127.0.0.1:5199to issue commands likeshowServers()ortopQueries()live.- The two tuning directives increase the number of simultaneous TCP connections and UDP queue slots. DoH uses TCP under the hood, so bumping
setMaxTCPClientThreadsavoids bottlenecks with many clients.
A few things to check: the DoH backend requires that your dnsdist server can reach the internet on port 443. No special firewall exceptions on the LAN side, since clients talk plain DNS to dnsdist. Ensure your system’s time is accurate; TLS certificate verification will fail if the clock is way off. Enable NTP if you haven’t.
If you prefer Quad9 with malware filtering, replace the url with "https://dns.quad9.net/dns-query". If you want to use multiple upstreams for redundancy, add two newServer lines and use setServerPolicy(firstAvailable) to fall back if one fails. That looks like:
newServer({url="https://dns.quad9.net/dns-query", pool="doh", order=1})
newServer({url="https://cloudflare-dns.com/dns-query", pool="doh", order=2})
setServerPolicy(FirstAvailable)
FirstAvailable sends all queries to the first server; if it becomes unresponsive, it moves to the next. No load balancing, but that’s fine for home use.
Start dnsdist: sudo systemctl start dnsdist. Check its status: sudo systemctl status dnsdist. Look for “Listening on” lines and any TLS errors. If the DoH endpoint is unreachable, you’ll see connection refused or SSL errors in journalctl -u dnsdist -f.
Testing and Verification
From another machine on your LAN, run a query:
dig @192.168.1.2 example.com
You should get an answer with the AD flag if DNSSEC validation is supported by the upstream. Check response time; DNS-over-HTTPS adds a few extra milliseconds compared to plain UDP, but it’s usually within 20–30 ms for the first lookup, then cached.
Test a non-existent domain to make sure the resolver isn’t hijacking NXDOMAIN responses:
dig @192.168.1.2 definitely-not-a-real-domain-12345.net
If you get an actual NXDOMAIN, great. If you get an IP pointing to a search page, your upstream is doing something fishy. Both Cloudflare and Quad9 return clean NXDOMAIN.
On the dnsdist server, connect to the console to see real-time stats:
sudo dnsdist -c 127.0.0.1:5199
> showServers()
This will list backends, their latency, and query counts. Watch for timeouts or SSL errors. If the DoH backend shows “down”, check connectivity and certificate verification. You can run tcpdump on port 443 to see if TLS handshakes are happening:
sudo tcpdump -i eth0 port 443
You’ll see connections to the resolver’s IP on 443.
To validate that queries really are encrypted from dnsdist to upstream, you can capture packets on the WAN interface (or the internet-facing interface of your router). The DNS payload won’t be readable; you’ll just see HTTPS traffic. However, note that dnsdist itself does not encrypt client-facing queries—those are still plain on your LAN. That’s usually fine unless you have an adversarial device on your local network. If you need LAN encryption too, you can set up DoT or DoH on dnsdist’s frontend and configure clients to use that, but that’s a different article.
Integrating with Your Home Network
The final step is telling your devices to use dnsdist. The cleanest approach is DHCP options. On your router’s DHCP server, set the primary DNS server to your dnsdist IP (192.168.1.2). Leave secondary DNS blank, or point it to another dnsdist instance if you’re running redundancy; don’t mix encrypted and unencrypted paths, or your system may leak queries.
If your router’s DHCP configuration doesn’t let you set custom DNS servers (common in ISP-supplied combiners), you have two options:
- Disable DHCP on the router entirely and run your own DHCP server. dnsmasq handles this beautifully and you can point its upstream address to dnsdist. That also gives you local name resolution for your machines.
- Manually configure each device’s DNS. Not recommended, but works for a laptop or two.
Once DHCP is updated, renew leases on clients (or reboot them). Check that they’re using the new DNS with nslookup or systemd-resolve --status. You should see 192.168.1.2 as the sole DNS server.
For IoT devices that ignore DHCP DNS settings, you might need firewall rules on the router that redirect all outbound DNS traffic (port 53) to your dnsdist box. That’s a more aggressive step but ensures no leaks. I’d recommend it only after you’re confident dnsdist is stable, because a redirect can create nasty loops if misconfigured.
Maintenance and Backup
dnsdist is relatively fire-and-forget, but like any proxy, it needs occasional attention. The main configuration file in /etc/dnsdist/dnsdist.conf should be backed up regularly. I’d suggest a cron job that copies it to a safe location once a day:
0 3 * * * cp /etc/dnsdist/dnsdist.conf /home/youruser/backups/dnsdist-$(date +\%F).conf
Even better, version it with git. If an update changes the syntax, having revision history is helpful.
Updating dnsdist: as it’s from the PowerDNS repo, apt upgrade will pull new versions. Major version bumps occasionally change configuration directives. Read the release notes before upgrading. A quick test on a secondary instance or VM before updating your primary DNS is smart. If you don’t have a spare, at least schedule the upgrade when you’re around to troubleshoot.
Monitor logs: journalctl -u dnsdist -f will show you startup issues, certificate warnings, or other problems. If your upstream provider rotates TLS certificates, things will keep working because dnsdist uses the system’s CA store, but keep the OS updated to avoid cert expiry issues.
One subtlety: dnsdist does not, by default, cache responses. It passes queries through to the upstream. For a home network, the upstream’s own caching plus client-side caching (like systemd-resolved or browser DNS caches) is usually sufficient. If you want dnsdist to cache, you can enable the packet cache by adding pc = newPacketCache(10000) and getPool("doh"):setCache(pc). That will store 10,000 responses in memory, cutting latency for frequently visited domains. Just be aware that cache poisoning risks are minimal at home, but do set staleCacheEntriesTTL appropriately if you go that route.
Backup strategy isn’t just about the config file. Keep a note of which upstream DoH URL you’re using, any ACLs, and any special routing rules. If the server dies, you can rebuild quickly. In a pinch, temporarily point DHCP back at the router’s DNS to restore internet access while you fix dnsdist.
Conclusion
Setting up a local DNS-over-HTTPS proxy with dnsdist gives your home network encrypted DNS without requiring per-device configuration. The setup is minimal—one config file, one upstream resolver, and a DHCP tweak. You get the privacy benefits of DoH and a single point to apply custom rules, all with minimal overhead. The trade-off is that you’ve introduced a new dependency: if the dnsdist box goes down, DNS resolution stops for the whole network. That’s why backups of the config, a clear rollback plan, and maybe a secondary resolver are worth thinking about from day one. But for the average household, this is a quiet, reliable way to take control of your DNS privacy.