Unbound Raspberry Pi: Complete Recursive DNS and Pi-hole Setup Guide

Private recursive dns with unbound & pi hole on raspberry pi

Unbound Raspberry Pi turns a Pi 4 into a private recursive DNS resolver that walks the DNS hierarchy from root servers to the authoritative nameserver for every query, with no upstream forwarder involved. Combined with Pi-hole as the network-wide ad blocker, the stack gives you DNSSEC validation, full query privacy, and ad blocking without any DNS query leaving your network in plaintext. This guide covers Pi-hole installation, Unbound installation and configuration, DNSSEC, connecting the two services, network router configuration, and maintenance.

Last tested: Raspberry Pi OS Bookworm Lite 64-bit | May 3, 2026 | Raspberry Pi 4 Model B (2GB) | Unbound 1.19 | Pi-hole 5.18 | FTL 5.25

Key Takeaways

  • Unbound runs on port 5335 and Pi-hole runs on port 53. They must be on the same Pi. Pi-hole receives DNS queries from your devices, filters ad and tracking domains, then forwards allowed queries to Unbound on port 5335. Unbound resolves those queries recursively without contacting any upstream forwarder.
  • DNSSEC validation runs in Unbound, not Pi-hole. Pi-hole’s DNSSEC toggle only displays validation status in the dashboard. The actual cryptographic validation happens in Unbound via the auto-trust-anchor-file directive. Do not enable DNSSEC in Pi-hole upstream settings when Unbound is handling it.
  • Point your router DHCP DNS setting to the Pi static IP. Do not use 8.8.8.8 or 1.1.1.1 alongside Unbound. If you leave those forwarders active in Pi-hole’s upstream settings alongside Unbound, queries will bypass Unbound for any domain Pi-hole cannot resolve from cache.

Unbound Raspberry Pi: How the Stack Works

When a device on your network needs to resolve example.com, the query goes to Pi-hole on port 53. Pi-hole checks whether the domain is on a blocklist. If it is, Pi-hole returns a null response. If not, Pi-hole forwards the query to Unbound on port 5335. Unbound starts at the root DNS servers, follows the delegation chain down through the TLD nameserver to the authoritative nameserver for example.com, and returns the answer. No upstream DNS provider like Google or Cloudflare is involved at any point.

DNSSEC validation occurs during Unbound’s recursive resolution. Unbound checks the cryptographic signatures at each delegation step, from root to TLD to authoritative nameserver. If the chain of trust breaks (because an answer was tampered with or a zone is misconfigured), Unbound returns SERVFAIL rather than the potentially compromised answer.

Hardware and OS Preparation

A Pi 4 with 2GB RAM handles this stack without measurable memory pressure. A Pi 3B+ works for a small household. A Pi Zero 2 W handles the load but leaves limited headroom if you run other services on the same device. Any of these run cooler and draw less power than a desktop machine running the same software.

Flash Raspberry Pi OS Bookworm Lite 64-bit using Raspberry Pi Imager. In the advanced settings, set hostname, enable SSH, and configure credentials. After first boot:

sudo apt update && sudo apt full-upgrade -y

Set a static IP so your router always points DNS to the same address:

sudo nmcli connection modify "Wired connection 1"   ipv4.method manual   ipv4.addresses 192.168.1.53/24   ipv4.gateway 192.168.1.1   ipv4.dns 127.0.0.1
sudo nmcli connection up "Wired connection 1"

Setting ipv4.dns 127.0.0.1 makes the Pi resolve its own DNS queries through Pi-hole, which is running locally. This is intentional. The Pi itself uses the stack it is serving.

Installing Pi-hole

Pi-hole provides the network-wide DNS filtering, dashboard, and query log. Install it using the official script:

curl -sSL https://install.pi-hole.net | bash

The installer is interactive. When asked for an upstream DNS provider during setup, select any option. You will replace it with Unbound after both services are running. Select your network interface when prompted. Accept the default blocklist. Write down the admin password shown at the end of the install.

Expected result: The install completes and confirms Pi-hole is running. Navigate to http://<pi-ip>/admin and the Pi-hole dashboard loads. The query log may show some initial activity as the Pi itself starts resolving through Pi-hole.

Installing and Configuring Unbound

sudo apt install unbound -y

Download the root hints file. This lists the IP addresses of the 13 root DNS server clusters that Unbound contacts to start each recursive resolution:

sudo wget -O /var/lib/unbound/root.hints   https://www.internic.net/domain/named.cache

Create /etc/unbound/unbound.conf.d/pi-hole.conf:

server:
    verbosity: 0
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    do-ip6: no

    root-hints: "/var/lib/unbound/root.hints"
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # Privacy hardening
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes

    # DNSSEC
    val-permissive-mode: no
    aggressive-nsec: yes
    val-clean-additional: yes

    # Performance
    prefetch: yes
    prefetch-key: yes
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    serve-expired: yes
    serve-expired-ttl: 86400
    rrset-cache-size: 256m
    msg-cache-size: 128m
    rrset-roundrobin: yes

    # Access control
    access-control: 127.0.0.0/8 allow
    access-control: 0.0.0.0/0 refuse
    do-not-query-localhost: no

    # Unprivileged operation
    username: "unbound"

Key settings explained: interface: 127.0.0.1 restricts Unbound to localhost only. No device on the network can query it directly. port: 5335 avoids conflict with Pi-hole on port 53. qname-minimisation sends only the minimum necessary query information to each nameserver in the chain, improving privacy. auto-trust-anchor-file tells Unbound where to store and maintain the DNSSEC root trust anchor automatically.

sudo systemctl enable --now unbound
sudo systemctl status unbound

Test Unbound directly:

# Basic resolution test
dig @127.0.0.1 -p 5335 google.com A

# DNSSEC success test (should return NOERROR)
dig @127.0.0.1 -p 5335 sigok.verteiltesysteme.net

# DNSSEC failure test (should return SERVFAIL)
dig @127.0.0.1 -p 5335 sigfail.verteiltesysteme.net

Expected result: The basic resolution returns an IP address. The DNSSEC success test returns NOERROR. The DNSSEC failure test returns SERVFAIL. This confirms Unbound is actively rejecting tampered DNS responses rather than silently returning bad data.

Unbound Raspberry Pi DNS query flow diagram showing device Pi-hole Unbound root servers recursive resolution path

Connecting Pi-hole to Unbound

In the Pi-hole admin interface at http://<pi-ip>/admin, go to Settings > DNS. Under Upstream DNS Servers:

  • Uncheck all pre-configured upstream providers (Google, Cloudflare, etc.)
  • In the Custom 1 (IPv4) field enter: 127.0.0.1#5335

The #5335 syntax tells Pi-hole to send queries to port 5335 rather than the default port 53. Leave all other upstream providers unchecked. Any active upstream forwarder alongside Unbound will receive queries for domains Pi-hole cannot answer from cache, defeating the purpose of recursive resolution.

Scroll down and save. Test the full stack:

# Query through Pi-hole (port 53) which forwards to Unbound
dig @127.0.0.1 google.com

# Check the Pi-hole query log to confirm it shows the query
# Admin > Query Log - should show the query with status NOERROR

Expected result: The dig query returns an IP address. The Pi-hole query log shows the query and its response. The query status column shows OK for non-blocked domains. If the query fails or returns SERVFAIL, check the Unbound service status and the port 5335 configuration.

Router and Network Configuration

For all devices on your network to use Pi-hole and Unbound automatically, set the router’s DHCP server to advertise the Pi’s static IP as the DNS server. The location of this setting varies by router. It is usually under DHCP settings or LAN settings. Set the primary DNS to the Pi IP (for example, 192.168.1.53) and leave the secondary DNS blank or set it to the Pi’s IP again.

After changing the router setting, devices need to renew their DHCP lease to receive the new DNS server. On most devices this happens within a few minutes, or you can disconnect and reconnect to Wi-Fi to force an immediate renewal.

Confirm a network device is using Pi-hole by checking the Pi-hole query log. Its queries should appear there. Alternatively:

# From a device on the network, check which DNS server it is using
nslookup google.com
# The Server: line should show the Pi's IP address

# Or on Linux:
resolvectl status

If you cannot change the router’s DHCP DNS settings (some ISP routers lock this), configure DNS manually on each device to point to the Pi’s IP, or enable Pi-hole’s built-in DHCP server and disable the router’s DHCP server entirely.

Maintenance

Keeping root hints current

Root hints change rarely but should be refreshed every few months. Add a cron job:

# Add to root crontab (sudo crontab -e):
# 0 3 1 * * wget -qO /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache && systemctl restart unbound

Updating both services

# Update Unbound via apt
sudo apt update && sudo apt upgrade unbound -y

# Update Pi-hole
pihole -up

# Update Pi-hole blocklists
pihole -g

Flushing the Unbound cache

# Flush a specific domain
sudo unbound-control flush example.com

# Flush everything
sudo unbound-control flush_zone .

Systemd hardening

Tighten Unbound’s system access with a service override:

sudo systemctl edit unbound

Add:

[Service]
ProtectSystem=full
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=true
sudo systemctl daemon-reload
sudo systemctl restart unbound

Troubleshooting

Unbound not starting

sudo systemctl status unbound
journalctl -u unbound -n 30

# Check config syntax
sudo unbound-checkconf /etc/unbound/unbound.conf.d/pi-hole.conf

unbound-checkconf reports the exact line causing a syntax error. The most common config mistakes are missing quotes around file paths, wrong indentation, and duplicate directives when using drop-in config files alongside the main unbound.conf.

Pi-hole not forwarding to Unbound

# Test Unbound directly
dig @127.0.0.1 -p 5335 google.com

# Test Pi-hole on port 53
dig @127.0.0.1 google.com

# Check Pi-hole upstream setting
# Admin > Settings > DNS > Custom 1 should show 127.0.0.1#5335

If Unbound responds on port 5335 but Pi-hole does not forward to it, the upstream setting in Pi-hole was not saved correctly. Re-enter 127.0.0.1#5335 in the Custom 1 field and save again. Ensure all pre-configured upstream providers are unchecked.

DNSSEC failures for valid domains

# Check if the domain validates correctly at root servers
dig +dnssec @8.8.8.8 problematic-domain.com

# Temporarily test with DNSSEC disabled in Unbound
# Set val-permissive-mode: yes in the config, restart, test, then set it back to no

Some domains have misconfigured DNSSEC and will always fail validation. If dig +dnssec @8.8.8.8 also returns SERVFAIL, the problem is the domain’s DNSSEC configuration, not Unbound. If dig @8.8.8.8 (without +dnssec) succeeds but Unbound returns SERVFAIL, Unbound is correctly rejecting a DNSSEC failure. Add a domain exception in Unbound only if you are certain the domain is legitimate and has a known DNSSEC misconfiguration.

All DNS resolution stops working

sudo systemctl status unbound
sudo systemctl status pihole-FTL

# Quick recovery: restart both in order
sudo systemctl restart unbound
sudo systemctl restart pihole-FTL

If the Pi reboots and DNS does not recover, the most common cause is that Pi-hole starts before Unbound has fully initialised. Add a service dependency to ensure ordering:

# Check current startup order
systemctl list-dependencies pihole-FTL

# If needed, add After=unbound.service to pihole-FTL override
sudo systemctl edit pihole-FTL

FAQ

Is this setup faster than using 1.1.1.1 or 8.8.8.8?

For uncached queries, slightly slower. Recursive resolution requires multiple round trips to root, TLD, and authoritative nameservers, whereas a forwarding DNS like 1.1.1.1 likely has the answer cached already. For cached queries (which is most queries after the first few minutes of operation), Unbound is faster because the answer comes from local memory with zero network latency. The performance difference is not noticeable in daily use.

Can I run Unbound without Pi-hole?

Yes. Unbound works as a standalone recursive resolver. Change interface: 127.0.0.1 to interface: 0.0.0.0 and add an access control rule for your LAN subnet, then point your router’s DHCP DNS to the Pi directly. You lose ad blocking but gain the same recursive resolution and DNSSEC validation. For ad blocking without the Unbound layer, Pi-hole alone with a public upstream like 1.1.1.1 is simpler and sufficient for most households.

What happens if the Pi goes offline?

All DNS resolution on your network stops until the Pi recovers or devices fall back to an alternate DNS. The fallback bypasses Pi-hole’s filtering when active, but it prevents complete network outage during Pi reboots or maintenance.

How does this compare to AdGuard Home?

AdGuard Home is an alternative to Pi-hole that includes built-in support for encrypted DNS protocols (DoH, DoT) without needing a separate resolver. Pi-hole with Unbound gives recursive privacy through a different architecture. Unbound walks the DNS tree rather than encrypting queries to a provider. AdGuard Home is simpler to set up for encrypted DNS; Pi-hole with Unbound gives you more transparency about exactly which servers your queries reach. For a full comparison, see AdGuard Home vs Pi-hole on Raspberry Pi.

Does DNSSEC protect against all DNS attacks?

DNSSEC protects against DNS cache poisoning and response spoofing by verifying cryptographic signatures on DNS records. It does not encrypt queries in transit. A network observer can still see which domains you are resolving. It does not prevent a legitimate but malicious domain from resolving correctly. And it does not protect against DNS-over-HTTPS bypass, where applications query DoH servers directly instead of using the system resolver. DNSSEC is one layer of DNS integrity, not a complete privacy solution.

References


About the Author

Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.

Last tested hardware: Raspberry Pi 4 Model B (2GB). Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. Unbound 1.19, Pi-hole 5.18.