Pi-hole + Unbound on a Raspberry Pi
Set up Pi-hole as a network-wide DNS blocker paired with Unbound for fully recursive, DNSSEC-validating resolution on a Raspberry Pi — no upstream DNS provider needed.
Before you start
- ▸Raspberry Pi (any model with 512 MB+ RAM) running Raspberry Pi OS Bookworm or a Debian/Ubuntu equivalent
- ▸Pi assigned a static IP address on your LAN (DHCP reservation or manual configuration)
- ▸SSH or keyboard/monitor access to the Pi with sudo privileges
- ▸Router admin access to change DHCP DNS settings
Pi-hole turns any Linux host into a network-wide DNS sinkhole, dropping ads and trackers before they reach any device on your LAN. Pairing it with Unbound gives you a fully recursive, validating resolver — your Pi queries the root nameservers directly instead of forwarding everything to a third-party like 8.8.8.8. The result is faster cold-cache lookups, full DNSSEC validation, and zero DNS data leaking to an upstream provider.
This guide targets a Raspberry Pi running Raspberry Pi OS (Debian Bookworm base), but every step works on any Debian/Ubuntu ARM or x86 host. You will end up with Pi-hole handling local blocking, Unbound handling all recursive resolution, and your router pointing every LAN client at the Pi.
Prerequisites and Network Notes
Give the Pi a static IP before you start. Either configure a DHCP reservation on your router (preferred — easier to change later) or set a static address in /etc/dhcpcd.conf or via nmcli. Write that address down; you will need it when configuring your router.
Pi-hole's web interface listens on port 80. If anything else occupies port 80 (lighttpd conflicts, another web server) resolve that first. Pi-hole's installer deploys its own lighttpd instance.
Step 1 — Install Pi-hole
Pi-hole provides a one-line installer that works reliably on Raspberry Pi OS, Ubuntu, and Debian. Review the script at github.com/pi-hole/pi-hole before running it if that is your policy.
curl -sSL https://install.pi-hole.net | bash
The interactive installer will ask several questions. Make these choices deliberately:
- Static IP: Confirm the IP you already set.
- Upstream DNS: Pick any provider for now — you will replace it with Unbound in a later step.
- Blocklists: Accept the defaults; you can add more lists later from the web UI.
- Web admin interface: Install it — essential for monitoring.
- Log queries: Yes, at least initially.
When the installer finishes, it prints the web interface URL and a randomly generated admin password. Save that password immediately.
# Change the admin password any time
pihole -a -p
Step 2 — Install and Configure Unbound
Install the package
sudo apt update && sudo apt install unbound -y
Download the root hints file
Unbound needs the list of DNS root nameservers. This file changes rarely but should be refreshed periodically.
sudo curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
Create the Pi-hole-specific Unbound config
Drop a dedicated config file for Pi-hole integration. Do not edit /etc/unbound/unbound.conf directly; files in unbound.conf.d/ are included automatically and survive package upgrades cleanly.
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
Paste the following. The key points: Unbound listens on port 5335 (Pi-hole owns 53), only on localhost, and DNSSEC validation is enabled.
server:
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
# Root hints
root-hints: /var/lib/unbound/root.hints
# Only answer from localhost
access-control: 127.0.0.1/32 allow
# Hide version and identity
hide-identity: yes
hide-version: yes
# DNSSEC
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
# Performance
edns-buffer-size: 1232
prefetch: yes
num-threads: 1
so-rcvbuf: 1m
# Private address ranges — never leak these upstream
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
Enable and start Unbound
sudo systemctl enable unbound
sudo systemctl restart unbound
sudo systemctl status unbound
Verify Unbound is resolving
dig pi-hole.net @127.0.0.1 -p 5335
You should see a NOERROR status and an answer section. If you see SERVFAIL check sudo journalctl -u unbound -n 50 for detail.
Step 3 — Point Pi-hole at Unbound
Log into the Pi-hole web admin at http://<your-pi-ip>/admin. Navigate to Settings → DNS. Under Upstream DNS Servers:
- Uncheck every preset provider (Google, Cloudflare, etc.).
- In the Custom 1 (IPv4) field enter:
127.0.0.1#5335
Scroll down and make sure Use DNSSEC is checked (Pi-hole passes the AD flag through; Unbound does the actual validation). Save. Pi-hole will restart its DNS service automatically.
Alternatively, edit the config file directly:
sudo nano /etc/pihole/setupVars.conf
Set or confirm these lines:
PIHOLE_DNS_1=127.0.0.1#5335
PIHOLE_DNS_2=
DNSSEC=true
Then apply:
sudo pihole restartdns
Step 4 — Configure Your Router
Every device on your network must use the Pi as its DNS server. The cleanest approach is to change the DNS server your router advertises via DHCP — one change covers every client automatically.
Log into your router admin interface (commonly 192.168.1.1 or 192.168.0.1). Find the DHCP server settings and set the Primary DNS to your Pi's static IP. Set the secondary DNS to the same IP or leave it blank — setting a fallback to 8.8.8.8 will bypass Pi-hole whenever the Pi is slow, defeating the purpose.
After saving, force clients to renew their DHCP lease or simply wait for leases to expire. On Linux clients you can renew immediately:
# systemd-networkd managed
sudo networkctl renew eth0
# NetworkManager managed
nmcli con down "Your Connection" && nmcli con up "Your Connection"
Step 5 — Verification
Confirm DNS resolution path
# From a LAN client — should show your Pi's IP as the server
resolvectl status
# Or with dig
dig example.com
Test DNSSEC validation
# This domain deliberately fails DNSSEC — expect SERVFAIL
dig dnssec-failed.org @127.0.0.1 -p 5335
# This domain passes — expect NOERROR with ad flag
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335 +dnssec
Confirm Pi-hole is blocking
# Should return 0.0.0.0 (blocked)
dig doubleclick.net
Monitoring with the Pi-hole Dashboard
The web dashboard at http://<pi-ip>/admin shows real-time query rates, top blocked domains, top clients, and query logs. For lightweight terminal monitoring:
# Live query log in the terminal
pihole -t
# Summary statistics
pihole -c
Pi-hole logs query data to a local FTL (Faster Than Light) database at /etc/pihole/pihole-FTL.db. Disk writes can add up on an SD card. If longevity is a concern, reduce log retention under Settings → System or configure FTL config to store the database in RAM using DBFILE=/tmp/pihole-FTL.db (data lost on reboot).
Keeping Things Updated
# Update Pi-hole core, web interface, and FTL
pihole -up
# Refresh blocklists
pihole -g
# Refresh root hints (add this to a monthly cron or systemd timer)
sudo curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
sudo systemctl restart unbound
Troubleshooting
- Unbound fails to start: Run
sudo unbound-checkconf /etc/unbound/unbound.conf.d/pi-hole.conf— it reports exact syntax errors. - Pi-hole shows "DNS resolution is currently unavailable": Unbound is likely not running or not listening on port 5335. Check
sudo ss -tulnp | grep 5335andsudo systemctl status unbound. - DNSSEC failures for legitimate domains: Usually a clock skew issue. Install
chronyorsystemd-timesyncdand ensure the Pi's clock is accurate. DNSSEC signatures are time-sensitive. - A device ignores Pi-hole DNS: Some smart TVs and Android devices hard-code 8.8.8.8. The only fix is a firewall rule on your router to intercept or block outbound UDP/TCP port 53 to any destination other than the Pi, redirecting it to the Pi.
- Slow first queries: Expected. Unbound starts with an empty cache and must walk the DNS tree from root. Subsequent queries for cached records are fast. Enable
prefetch: yes(already in the config above) to warm the cache proactively.
Frequently asked questions
- Why use Unbound instead of just forwarding to Cloudflare or Google?
- Forwarding DNS means a third party sees every domain your network queries. Unbound resolves recursively from the root, so no single upstream provider has a complete picture of your DNS traffic. You also get full DNSSEC validation rather than trusting the forwarder's claim of validation.
- Will this break if the Raspberry Pi goes offline?
- Yes — all DNS on your network will fail, which means most internet browsing stops. Keep the Pi reliable with a quality power supply and a good SD card or USB SSD. For resilience, run a second Pi-hole instance and set it as the secondary DHCP DNS.
- Can I run Pi-hole and Unbound on the same port 53?
- No. Pi-hole's FTL process owns port 53. Unbound is intentionally configured on port 5335 here so the two services co-exist. Pi-hole intercepts client queries on 53, filters them, then forwards allowed queries to Unbound on 5335.
- How do I whitelist a domain that Pi-hole is incorrectly blocking?
- Use the web admin under Whitelist, or run: pihole -w yourdomain.com from the terminal. The change takes effect immediately without a restart.
- Does this work with IPv6?
- Pi-hole supports IPv6. The Unbound config above disables IPv6 (do-ip6: no) to keep things simple on networks where IPv6 is not configured. If your network uses IPv6, enable it in the Unbound config, add an IPv6 listen interface, and set the Pi-hole custom upstream to ::1#5335 in addition to the IPv4 entry.
Related guides
Build a Mesh VPN with Nebula
Build a fully self-hosted mesh VPN with Nebula: create a CA, sign node certs, configure lighthouses, enforce group-based firewall rules, and run as a systemd service.
Common Linux Network Ports Reference
Learn Linux port ranges, read /etc/services, find what's listening with ss and nmap, and apply solid firewall rules to expose or block the right ports.
How to Configure a Static IP on Linux
Configure a static IP on Linux using Netplan, NetworkManager (nmcli), or systemd-networkd across Ubuntu, Fedora, Debian, and Arch with verified steps.
Expose a Service with Cloudflare Tunnel
Expose local services to the internet without port-forwarding using Cloudflare Tunnel. Install cloudflared, create a named tunnel, configure ingress rules, and run as a systemd service.