How to Set Up Unbound as a Recursive DNS Resolver on Raspberry Pi
When you type a domain name into your browser, your device sends a DNS query to a resolver to find the corresponding IP address. Most people rely on third-party resolvers like Google (8.8.8.8) or Cloudflare (1.1.1.1) for this, but that means handing over your entire browsing history to those providers. Unbound lets you run your own recursive DNS resolver that queries the authoritative name servers directly, keeping your DNS lookups private.
What is Unbound?
Unbound is a lightweight, open-source DNS resolver developed by NLnet Labs. When configured as a recursive resolver, it does not forward queries to an upstream provider. Instead, it starts at the DNS root servers and works its way down the hierarchy to resolve each query. This means:
- No third-party DNS provider sees your queries
- You get DNSSEC validation out of the box
- Responses are cached locally for faster repeat lookups
- You have full control over your DNS resolution chain
Prerequisites
Before you begin, make sure you have:
- A Raspberry Pi (any model) running Raspberry Pi OS
- SSH access or a terminal session on the Pi
- Root or sudo privileges
- Port 53 available (if Pi-hole is running, we will configure it to use Unbound as upstream later)
Step 1: Update Your System
sudo apt update && sudo apt upgrade -y
Step 2: Install Unbound
Install Unbound from the default Raspberry Pi OS repositories:
sudo apt install -y unbound
Unbound will be installed and started automatically. However, it may fail to start initially if port 53 is already in use. That is fine -- we will configure it properly before restarting.
Step 3: Download Root Hints
The root hints file tells Unbound where to find the DNS root servers. Download the latest version:
sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
It is good practice to update this file periodically. Add a monthly cron job with sudo crontab -e:
0 3 1 * * wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root && systemctl restart unbound
Step 4: Create the Unbound Configuration
Create a custom configuration file:
sudo nano /etc/unbound/unbound.conf.d/pi-config.conf
Paste the following configuration:
server:
verbosity: 0
interface: 0.0.0.0
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
prefer-ip6: no
root-hints: "/var/lib/unbound/root.hints"
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 1
so-rcvbuf: 1m
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
Key settings: port 5335 avoids conflicts with Pi-hole on port 53 (change to 53 if running standalone). harden-glue and harden-dnssec-stripped enable DNSSEC validation. prefetch refreshes cached entries before expiry. The private-address entries prevent DNS rebinding attacks.
Step 5: Test the Configuration and Restart Unbound
Verify the configuration, restart Unbound, and check its status:
sudo unbound-checkconf /etc/unbound/unbound.conf.d/pi-config.conf
sudo systemctl restart unbound
sudo systemctl status unbound
The first command should report no errors. The status command should show Unbound as active and running.
Step 6: Test DNS Resolution
Use the dig command to verify Unbound is resolving queries correctly. Query Unbound directly on port 5335:
dig example.com @127.0.0.1 -p 5335
You should see an ANSWER SECTION with the IP address for example.com and status: NOERROR in the header. The first query may be slower as Unbound builds its cache.
Test DNSSEC validation with these two commands:
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335
The first should return status: SERVFAIL (invalid DNSSEC signature correctly rejected). The second should return status: NOERROR (valid signature accepted).
Step 7: Integrate with Pi-hole
If you are running Pi-hole on the same Raspberry Pi, you can configure it to use Unbound as its upstream DNS resolver. This gives you the best of both worlds: Pi-hole handles ad blocking and Unbound handles recursive resolution. For Pi-hole installation instructions, see our Pi-hole guide.
- Open the Pi-hole admin interface
- Go to Settings then DNS
- Uncheck all upstream DNS servers (Google, Cloudflare, etc.)
- Under Custom 1 (IPv4), enter
127.0.0.1#5335 - Click Save
Pi-hole will now send all its DNS queries to your local Unbound instance instead of a third-party resolver.
Troubleshooting
- Unbound fails to start: Run
sudo unbound-checkconfto find configuration errors. Also checkjournalctl -u unboundfor detailed error messages. - Port conflict on 53: If another service is using port 53, keep Unbound on port 5335 and configure clients or Pi-hole to point to that port.
- Slow first queries: This is normal. Unbound needs to traverse the DNS hierarchy from the root servers on the first lookup. Subsequent queries for the same domain are served from cache.
- dig command not found: Install the
dnsutilspackage withsudo apt install -y dnsutils. - Pi-hole not using Unbound: After changing the upstream DNS in Pi-hole, restart the Pi-hole DNS service with
pihole restartdnsto ensure the changes take effect.
Conclusion
Running Unbound as a recursive DNS resolver on your Raspberry Pi gives you full control over your DNS resolution. Combined with Pi-hole for ad blocking, you get a private and efficient DNS setup that does not depend on any third-party provider. The entire DNS chain -- from root servers to your browser -- stays under your control.