Setting Up Unbound

The most private DNS setup you can run. Unbound resolves domain names by querying the root DNS servers directly — no third-party resolver ever sees your DNS traffic.

Recursive Resolver DNSSEC Maximum Privacy By NLnet Labs

How Unbound Works

When you use Google DNS (8.8.8.8) or Cloudflare (1.1.1.1), your DNS queries pass through their servers — they can see every domain you visit. Unbound cuts out the middleman entirely.

🔒 True DNS privacy — Unbound contacts root servers (like a.root-servers.net) directly, then follows the delegation chain to find the authoritative answer. No third party is involved.

🔗 Best paired with Pi-hole or AdGuard Home — use Unbound as the upstream resolver for Pi-hole/AdGuard to get both ad blocking AND full DNS privacy.

Step-by-Step Installation

01

Install Unbound

sudo apt update
sudo apt install unbound -y
02

Download Root Hints

Root hints tell Unbound where the root DNS servers are. These are automatically maintained but it's good practice to fetch a fresh copy:

sudo curl -o /var/lib/unbound/root.hints \
  https://www.internic.net/domain/named.root
03

Configure Unbound

Create a configuration file for Pi-hole/AdGuard integration (listening on port 5335 to avoid conflict with Pi-hole on port 53):

sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf

Paste the following configuration:

server:
    # Logging (disable for privacy)
    verbosity: 0

    # Interface and port
    interface: 127.0.0.1
    port: 5335

    # Allow queries only from localhost
    access-control: 127.0.0.1/32 allow

    # Root hints file
    root-hints: "/var/lib/unbound/root.hints"

    # DNSSEC validation
    harden-dnssec-stripped: yes
    use-caps-for-id: no

    # Privacy settings
    harden-glue: yes
    harden-referral-path: yes
    harden-algo-downgrade: no
    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

    # Performance
    prefetch: yes
    num-threads: 1
    so-rcvbuf: 1m
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    neg-cache-size: 4m

    # Minimal response size
    minimal-responses: yes
    val-log-level: 1
04

Start and Enable Unbound

sudo systemctl enable unbound
sudo systemctl restart unbound

# Check it's running without errors:
sudo systemctl status unbound
05

Test Unbound is Working

# Test a normal resolution (should return an IP):
dig pi-hole.net @127.0.0.1 -p 5335

# Test DNSSEC validation (should show "ad" flag):
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335

# Test DNSSEC rejection of bad signatures (should fail/SERVFAIL):
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335

✅ If the first two return results and the third returns SERVFAIL — DNSSEC is working correctly.

06

Point Pi-hole to Unbound

In Pi-hole admin: Settings → DNS. Uncheck all upstream DNS providers, then add a Custom DNS server:

Custom upstream DNS server 1: 127.0.0.1#5335

Click Save. Pi-hole now uses Unbound as its resolver — you have ad blocking AND recursive DNS privacy.

07

Point AdGuard Home to Unbound (Alternative)

If using AdGuard Home instead of Pi-hole: Settings → DNS Settings → Upstream DNS servers:

127.0.0.1:5335

Useful Commands

TaskCommand
Restart Unboundsudo systemctl restart unbound
View statussudo systemctl status unbound
View logssudo journalctl -u unbound -f
Check config syntaxunbound-checkconf
Update root hintssudo curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
Cache statssudo unbound-control stats_noreset
Flush cachesudo unbound-control flush_zone .

Architecture Overview

When paired together, your DNS stack looks like this:

📱 Client device → asks Pi-hole/AdGuard for DNS → Pi-hole checks blocklists → if not blocked, forwards to Unbound → Unbound queries root servers → answer returned to device. Every step happens on your local network.