Introduction
Ever get the feeling your internet knows a little too much about you? Yeah, same here. Every time you load a webpage, your device throws out a request like it’s shouting, “Hey, where’s google.com live?” And guess who’s listening? Your ISP, third-party DNS providers, maybe even that free coffee shop Wi-Fi you regretted using last week.
Now, imagine rerouting those nosey queries to your own private recursive DNS resolver running on a Raspberry Pi. That’s where Unbound steps in. Pair it with Pi-hole acting as an upstream filter, and you’ve got a setup that blocks ads, skips trackers, and never sends DNS logs to someone else’s servers. It’s your network traffic, your rules. Plus, it’s surprisingly light on resources—even that dusty Pi Zero in your drawer can handle it.
Key Takeaways
Choosing the Right Hardware and OS
Look, not all Raspberry Pis are created equal. You might think any old board will do, but if you’re planning to run Unbound with Pi-hole, there’s a sweet spot between power, heat, and “wait, why is nothing resolving?”
Raspberry Pi 4B is the crowd favorite. It’s got enough RAM to handle DNS cache, run Unbound without choking, and still give Pi-hole its share. Got a Pi Zero 2 W lying around? That’ll work too, just don’t expect blazing speeds with a hundred devices hammering it.
For OS, stick with Raspberry Pi OS Lite if you want something reliable and low on fluff. DietPi is even lighter if you’re feeling adventurous, and Ubuntu Server 22.04 is rock solid if you’re into that LTS life.
Here’s a quick table to compare:
Model | RAM | Use Case | Notes |
---|---|---|---|
Raspberry Pi 4B | 2–8GB | Main server | Great balance of power/efficiency |
Pi Zero 2 W | 512MB | Light usage, backup resolver | Needs passive cooling |
Pi 3B+ | 1GB | Medium load, basic home use | Gets warm under stress |
Pi 400 | 4GB | Desktop + DNS combo | Built-in keyboard, stays cool |
Spoiler alert: If you’re still using a Pi 1, stop reading and go fix your life.
Installing Unbound on Raspberry Pi
All right, let’s get Unbound actually doing something. You’ve got your Raspberry Pi booted up, SSH’d in, and probably already wondering if this is going to brick your internet. (It won’t—probably.)
Start by updating your packages. Depending on your OS, it’ll look something like this:
sudo apt update && sudo apt install unbound
Now, Unbound needs a list of root DNS servers to get going. This isn’t optional—without it, it’s like asking for directions without a map.
wget https://www.internic.net/domain/named.cache -O /var/lib/unbound/root.hints
Make sure Unbound is using this hints file by referencing it in /etc/unbound/unbound.conf
. You’ll need to open it up and actually write the config. Here’s the minimal bit to make it behave:
server:
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
root-hints: "/var/lib/unbound/root.hints"
Notice that port 5335? That’s because Pi-hole likes to hog port 53, so we give Unbound its own little corner.
Now check the service:
sudo systemctl enable unbound
sudo systemctl start unbound
And see if it’s alive:
dig @127.0.0.1 -p 5335 google.com
If it returns an IP? Congrats. You’re resolving like a grown-up.
Configuring Unbound for Recursive Resolution
Okay, so Unbound’s installed—but now it needs to act like a proper recursive resolver, not some clueless middleman. That means answering DNS queries by walking the whole DNS chain itself, not just phoning a friend (aka upstream forwarders).
Pop open the unbound.conf
file again:
sudo nano /etc/unbound/unbound.conf
And update it with more advanced goodies:
server:
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
root-hints: "/var/lib/unbound/root.hints"
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
qname-minimisation: yes
cache-min-ttl: 3600
cache-max-ttl: 86400
prefetch: yes
prefetch-key: yes
val-clean-additional: yes
aggressive-nsec: yes
val-permissive-mode: no
rrset-roundrobin: yes
serve-expired: yes
serve-expired-ttl: 86400
do-not-query-localhost: no
auto-trust-anchor-file: "/var/lib/unbound/root.key"
Then initialize DNSSEC (the digital signature checking system that keeps spoofers out of your DNS business):
sudo unbound-anchor -a /var/lib/unbound/root.key
Restart Unbound to let it soak all that in:
sudo systemctl restart unbound
What you’ve got now is a recursive DNS resolver that does its own dirty work, follows the root servers, and won’t ask Google or Cloudflare what 1.1.1.1 actually means.
Also: it’s not logging queries unless you tell it to, and it’s validating DNSSEC signatures, which means it knows when something smells phishy.
Setting Up Pi-hole to Use Unbound
Now that Unbound is strutting around like a DNS boss, it’s time to let Pi-hole know who’s in charge. And spoiler alert—it ain’t your ISP’s DNS anymore.
Fire up the Pi-hole Admin Web Interface, head to Settings > DNS, and in the Custom section at the bottom, enter this:
127.0.0.1#5335
That #5335
tells Pi-hole to send DNS queries to Unbound’s port, not the usual 53. They don’t share well, so we keep ’em separated like roommates with bad history.
Make sure to uncheck all other upstream DNS providers (Google, Cloudflare, etc.) if you want true recursive DNS without outside help. Otherwise, you’re just adding another middleman.
Now scroll down and hit Save. Give it a test:
dig pi-hole.net
Watch the results flow through Unbound via Pi-hole. If you see something like Query time: 50 msec
, congrats—your local recursive DNS resolver is doing its job.
Extra settings to double-check:
- DNSSEC: Enable it in the Pi-hole GUI to show validated lookups.
- Conditional Forwarding: Turn it OFF unless you like leaking internal domain traffic back to your router.
- Logs: Optional, but if you’re nosey like me, keep logging enabled just to peek at what’s resolving.
Now both systems are talking like old friends: Unbound handles the legwork, and Pi-hole slaps down ads and trackers with a smirk.
Performance Optimization
Let’s be honest—nobody likes slow DNS. Especially when you’re the one who set it up. Thankfully, Unbound isn’t just secure—it’s snappy, if you tell it how to behave.
Start with these cache-related settings in your unbound.conf
:
cache-min-ttl: 3600
cache-max-ttl: 86400
prefetch: yes
serve-expired: yes
serve-expired-ttl: 86400
cache-min-ttl
keeps answers for at least an hourcache-max-ttl
stretches out those good results for a full dayprefetch
grabs popular queries before they expireserve-expired
makes Unbound answer from cache even if the record is a little stale (better than a timeout)
Next, pump up the storage a bit:
rrset-cache-size: 256m
msg-cache-size: 128m
These bump the memory allocated to DNS results. If you’ve got a Raspberry Pi with more than 1GB RAM, use it. Your future self will thank you when 30 devices start asking about cdn.facebook.net
at the same time.
You can also control verbosity:
log-queries: no
verbosity: 0
Unless you enjoy watching your logs fill with 10,000 queries for TikTok, turn this off. It keeps the Pi-hole logs clean too, since Pi-hole only shows what Unbound passes back.
Want to go full paranoid? Add:
minimal-responses: yes
hide-identity: yes
hide-version: yes
These tell Unbound to mind its own business and not tell outsiders what version you’re running or who you are.
Securing the Resolver
You’ve built a DNS setup that works—but does it work for everyone on the internet? It shouldn’t. Unless you want to become the world’s slowest public DNS, it’s time to lock things down.
Limit Unbound to Localhost
This part’s already done if you used the default config:
interface: 127.0.0.1
This makes sure only Pi-hole on the same device can talk to Unbound. Nobody else on your network (or the internet) should be able to query it.
Access Control Lists
Want to be extra sure? Add an explicit rule:
access-control: 127.0.0.0/8 allow
access-control: ::1 allow
access-control: 0.0.0.0/0 refuse
access-control: ::0/0 refuse
This says, “Local IPs? Fine. Everything else? Get lost.”
Rate Limiting
If someone does find your DNS port, slow them down:
ratelimit: 1000
ip-ratelimit: 200
That’ll make Unbound throttle abuse instead of melting under load.
Run as Unprivileged User
By default, Unbound should drop root privileges:
username: "unbound"
Don’t skip this. If Unbound ever gets compromised (unlikely, but still), it can’t trash your whole system.
Systemd Hardening
You can make it even tighter by editing the service unit:
sudo systemctl edit unbound
Add:
[Service]
ProtectSystem=full
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=true
This limits Unbound’s access to parts of the filesystem it doesn’t need.
Monitoring and Logs
You’ve got a slick DNS setup—so now what? You watch it like a hawk, obviously. DNS breaks quietly, so you need to know what’s happening before your spouse says, “Why isn’t the internet working again?”
Check Logs the Old-Fashioned Way
journalctl -u unbound
tail -f /var/log/pihole.log
tail -f /var/log/pihole-FTL.log
Use Pi-hole’s Dashboard
The web UI under Query Log and Long-term Data shows:
- Who requested what
- Which domains got blocked
- Whether queries came from Unbound or another upstream
Third-Party Tools That Play Nice
- Netdata – Lightweight dashboard with DNS stats
- Grafana + Prometheus – Custom dashboards
- vnStat,
iftop
,nload
– Bandwidth tracking
Don’t Forget Rotation
Log files get big fast. Use logrotate
to keep things clean and avoid SD card death.
Troubleshooting Tips
All right, so things went sideways. DNS won’t resolve, your spouse is glaring at you, and even the dog looks disappointed. Here’s how to fix your Frankenstein setup without rage-quitting back to Google DNS.
- Check service status:
sudo systemctl status unbound
- Test Unbound directly:
dig @127.0.0.1 -p 5335 google.com
- Test DNSSEC:
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335
- Fix DNSSEC with:
sudo unbound-anchor -a /var/lib/unbound/root.key
- Flush cache:
sudo unbound-control flush_zone .
- Restart Unbound:
sudo systemctl restart unbound
Home Network Configuration
You’ve got Unbound resolving DNS and Pi-hole filtering like a champ—but none of that matters if your devices are still asking the router for directions.
- Change router’s DHCP DNS to your Pi-hole IP
- Or disable router DHCP and enable Pi-hole’s DHCP server
- Set a static IP in
/etc/dhcpcd.conf
- Add local DNS records in Pi-hole web UI
- Use Gravity Sync or Teleporter for redundancy
Maintenance and Community Help
Let’s be real—this setup’s not exactly “set it and forget it.”
Stay Updated
sudo apt update && sudo apt upgrade unbound
pihole -up
Backup with Teleporter from Pi-hole GUI to save:
- Blocklists
- Whitelists
- Local DNS records
- DHCP settings
Join the Nerd Herd
Don’t Sleep on Logs
journalctl -u unbound --since "7 days ago"
Check regularly to catch issues early.
Frequently Asked Questions (FAQ)
Q: Can I use Unbound without Pi-hole?
Yes, but you’ll lose ad-blocking and filtering.
Q: Do I need DNSSEC in both Unbound and Pi-hole?
Only Unbound handles validation. Pi-hole just displays the status.
Q: What happens if Unbound fails?
Pi-hole won’t resolve domains. Restart or use a temporary fallback DNS.
Q: Is this setup faster than using 1.1.1.1 or 8.8.8.8?
Yes, once caching kicks in. First query might be slower.
Q: Can I run this setup with other services on the same Pi?
Yes, as long as resources aren’t maxed out.
References
- Using Unbound as a Local Recursive Resolver
- Unbound Documentation – NLnet Labs
- Pi-hole GitHub Repository
- Unbound – Arch Wiki
- Guidelines for Encrypting DNS – Internet Society