$linuxjunkies
>

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.

IntermediateUbuntuDebianFedoraArch10 min readUpdated June 7, 2026

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 5335 and sudo systemctl status unbound.
  • DNSSEC failures for legitimate domains: Usually a clock skew issue. Install chrony or systemd-timesyncd and 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.
tested on:Debian 12 (Bookworm) on Raspberry Pi OSUbuntu 24.04 LTSDebian 11 (Bullseye)

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