After my positive first impressions with Pi-hole, I decided to take the next logical step: eliminating my dependence on external DNS resolvers entirely. While Quad9 served me well, there's something unsettling about routing all my DNS queries through a third party, no matter how trustworthy they appear.
The solution? Unbound - a validating, recursive, caching DNS resolver that can operate completely independently, resolving domain names directly from authoritative sources without relying on upstream DNS providers.
Why Make the Switch?
My motivation goes beyond just privacy paranoia (though that's certainly part of it):
Privacy First: Every DNS query reveals your browsing patterns. Even with Quad9's privacy promises, I prefer keeping that data entirely within my network.
Security Enhancement: Unbound performs DNSSEC validation by default, ensuring the authenticity of DNS responses and protecting against DNS spoofing attacks.
Reduced Latency: While counterintuitive, eliminating the round-trip to external resolvers can actually improve response times for frequently accessed domains through better local caching.
True Independence: No more wondering about logging policies, data retention, or potential government requests to DNS providers.
The Downsides to Consider
Before diving in, it's worth acknowledging the trade-offs:
Increased Complexity: You're now responsible for maintaining and troubleshooting your own DNS infrastructure. When things break, you can't just blame your ISP's DNS servers.
Initial Query Delays: Cold cache scenarios will be slower as Unbound has to walk the entire DNS hierarchy from root servers. First visits to new domains will take noticeably longer.
Resource Usage: While minimal, you're now running an additional service that consumes memory and CPU cycles on your Pi.
Potential Connectivity Issues: If your Pi goes down, your entire network loses DNS resolution. External resolvers provide redundancy that you're giving up.
CDN Sub-optimization: Content delivery networks may not route you to the geographically closest servers, potentially affecting streaming and download performance. Many large DNS providers like Cloudflare and Google use Anycast networks, where the same IP address is announced from multiple geographic locations, automatically routing you to the nearest server. When you run your own recursive resolver, you lose this geographic optimization and might connect to CDN endpoints that are further away.
False Sense of Security: While your DNS queries are now private, it's important to remember that all your actual web traffic (HTTP/HTTPS requests) still flows through your ISP and is visible in their logs. You've privatized the "phone book lookup" but not the actual "conversation" - your ISP can still see which IP addresses you're connecting to, just not the domain names that resolved to them.
Maintenance Overhead: The root hints file should be updated periodically (though it changes infrequently), and you're responsible for keeping your Unbound configuration current and secure.
The Verdict: Benefits Outweigh the Costs
After weighing these trade-offs, the privacy and security benefits still made a compelling case for proceeding. The complexity is manageable for anyone comfortable with basic Linux administration, and the performance impacts are largely theoretical for typical home usage. Most importantly, the peace of mind from knowing exactly where my DNS queries go and how they're handled proved worth the additional overhead.
The Setup Process
Installing and configuring Unbound alongside Pi-hole turned out to be surprisingly straightforward.
Step 1: Install Unbound
sudo apt update
sudo apt install unbound -y
Step 2: Configure Unbound for Security and Performance
I created a custom configuration at /etc/unbound/unbound.conf.d/pi-hole.conf
:
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
Here's my security-focused configuration:
server:
# Basic settings
port: 5335
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
# Security settings
trust-anchor-file: "/var/lib/unbound/root.key"
auto-trust-anchor-file: "/var/lib/unbound/root.key"
val-clean-additional: yes
val-permissive-mode: no
val-log-level: 1
# Privacy settings
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: yes
use-caps-for-id: yes
# Performance settings (security-first approach)
cache-min-ttl: 300
cache-max-ttl: 86400
prefetch: yes
prefetch-key: yes
# Interface settings
interface: 127.0.0.1
access-control: 127.0.0.1/32 allow
access-control: ::1 allow
# Logging
verbosity: 1
log-queries: no
log-replies: no
# Root hints
root-hints: "/var/lib/unbound/root.hints"
Step 3: Root Hints Management
Root hints are essential files that tell Unbound where to find the DNS root servers - the starting point for all DNS resolution. When installing Unbound via package manager, root hints are included and should be updated automatically through regular system updates.
Note: The root hints file changes infrequently (typically a few times per year when root servers are added, removed, or have IP changes). Rather than manual updates, it's best to keep your system updated through your package manager, which will handle root hints updates appropriately - roughly every 6 months is more than sufficient for most users.
Step 4: Initialize DNSSEC Root Key (Usually Optional)
Modern Unbound packages typically handle DNSSEC root key initialization automatically. However, if you encounter DNSSEC validation errors or want to ensure the key is properly configured, you can initialize it manually:
sudo unbound-anchor -a "/var/lib/unbound/root.key"
sudo chown unbound:unbound /var/lib/unbound/root.key
Note: If this step fails or the file already exists, it's likely already configured correctly by the package installation.
Step 5: Start and Enable Unbound
sudo systemctl enable unbound
sudo systemctl start unbound
Step 6: Test Unbound
Before integrating with Pi-hole, I verified Unbound was working with both valid and invalid domain lookups:
Testing with an existing domain:
robins@pi4:~ $ dig @127.0.0.1 -p 5335 google.com | egrep -w 'status|^google'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58713
google.com. 269 IN A 142.250.70.206
Testing with a non-existent domain:
dig @127.0.0.1 -p 5335 asdfasdsfasfdfsafasd | egrep -w 'status|flags'
robins@pi4:~ $ dig @127.0.0.1 -p 5335 asdfasdsfasfdfsafasd | egrep -w 'status|flags'
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 19691
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
; EDNS: version: 0, flags:; udp: 1232
Both tests confirm Unbound is working correctly. The existing domain test shows successful resolution with NOERROR
status, while the non-existent domain test returns NXDOMAIN
(Non-eXistent DOMAIN), which is the proper response when a domain doesn't exist. Note the ad
flag in the non-existent domain response, indicating that even failed lookups are DNSSEC-validated - Unbound cryptographically verified that this domain truly doesn't exist rather than just accepting an unvalidated negative response.
Step 7: Configure Pi-hole to Use Unbound
In the Pi-hole admin interface:
- Navigate to Settings → DNS
- Uncheck all upstream DNS servers
- Add
127.0.0.1#5335
as a custom DNS server - Enable DNSSEC validation
- Save settings
The Results: Worth the Effort
After 24 hours of operation with the new setup, the benefits are clear:
Complete Privacy: All DNS queries now resolve independently without touching external servers (except for the initial root server queries, which reveal no specific domain information).
DNSSEC Validation: Every response is cryptographically verified, providing protection against DNS manipulation.
Improved Cache Efficiency: Unbound's more sophisticated caching algorithm seems to provide better hit rates for our household's browsing patterns.
Performance Impact: Negligible. The Pi 4 handles both Pi-hole and Unbound without breaking a sweat:
robins@pi4:~ $ uptime
21:22:25 up 2 days, 9:47, 3 users, load average: 0.00, 0.00, 0.00
Security Considerations
My configuration prioritizes security over raw performance:
- Conservative TTL settings: Shorter cache times mean more frequent validation
- Strict DNSSEC validation: No permissive mode that might accept invalid responses
- Minimal logging: No query logging to preserve privacy even locally
- Restricted access: Only localhost can query Unbound directly
Final Thoughts
Setting up Unbound with Pi-hole represents the logical conclusion of taking control over your DNS infrastructure. While the privacy and security benefits are the primary motivators, the technical satisfaction of running a completely self-contained DNS resolution system is considerable.
The setup process is more involved than simply pointing Pi-hole at an external resolver, but the configuration is straightforward and well-documented. For anyone concerned about DNS privacy or wanting to reduce external dependencies, this combination provides an excellent solution.
The Pi 4 continues to prove itself as the perfect platform for this type of network infrastructure project - handling both Pi-hole and Unbound with resources to spare. As I mentioned in part 1, even a Pi 2 would likely suffice for most households, making this an accessible upgrade for anyone looking to enhance their home network's privacy and security posture.