Raspberry Pi with Unbound
Details about Pi-hole and Unbound setup.

Pi-hole + Unbound on Raspberry Pi 4 (Homelab Guide)

Network-wide ad blocking with a validating, recursive, caching DNS resolver.

Introduction

This document walks through step-by-step installation of Pi-hole (DNS sinkhole and ad blocker) on a Raspberry Pi 4, together with Unbound as a local recursive and validating DNS resolver. This combination ensures privacy, speed, and reliability – without relying on external DNS providers.

Required Hardware

Software Installation Steps

  1. Flash Raspberry Pi OS Lite (64‑bit) using Raspberry Pi Imager. Enable SSH and set a hostname.
  2. First login and system update:
    sudo apt update && sudo apt full-upgrade -y
    sudo reboot
  3. Set static IP address (example in dhcpcd.conf):
    sudo nano /etc/dhcpcd.conf
    
    # Example (adjust to your network):
    interface eth0
    static ip_address=192.168.1.10/24
    static routers=192.168.1.1
    static domain_name_servers=127.0.0.1

    Note: For LAN DNS clients, the DHCP gateway should advertise this Pi-hole server's IP.

  4. Install Pi-hole (official installer):
    curl -sSL https://install.pi-hole.net | bash

    During installation, select the interface (eth0), static IP, and temporary upstream DNS (e.g., Quad9). We will later switch to Unbound.

  5. Install Unbound:
    sudo apt install -y unbound
  6. Download root hints (optional cron for monthly refresh):
    sudo mkdir -p /var/lib/unbound
    sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
    # (Optional) monthly auto-refresh:
    echo '0 3 1 * * root wget -qO /var/lib/unbound/root.hints https://www.internic.net/domain/named.root' | sudo tee /etc/cron.d/unbound-root-hints

Configuration Steps

  1. Unbound configuration – create dedicated file:
    sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
    server:
      verbosity: 0
      logfile: "/var/log/unbound/unbound.log"
      interface: 127.0.0.1
      port: 5335
      do-daemonize: no
      use-caps-for-id: no
      edns-buffer-size: 1232
      prefetch: yes
      qname-minimisation: yes
      harden-dnssec-stripped: yes
      harden-glue: yes
      harden-referral-path: yes
      aggressive-nsec: yes
      root-hints: "/var/lib/unbound/root.hints"
      cache-min-ttl: 3600
      cache-max-ttl: 86400
      msg-cache-size: 128m
      rrset-cache-size: 256m
      so-rcvbuf: 1m
      so-sndbuf: 1m
      access-control: 127.0.0.0/8 allow

    Check config and start service:

    sudo unbound-checkconf
    sudo systemctl enable --now unbound
  2. Pi-hole → Upstream DNS = Unbound:

    In http://<IP-of-Pi>/adminSettings → DNS enable Custom 1 (IPv4) and set:

    127.0.0.1#5335

    Disable all other external upstream options so queries go only through Unbound.

  3. DNSSEC: With Unbound as validator, you usually do not enable DNSSEC inside Pi-hole (Unbound already validates).
  4. Disable conflicting services on port 53 (if any):
    sudo systemctl disable --now systemd-resolved

Verification / Tests

# Query Unbound directly
dig @127.0.0.1 -p 5335 example.com +dnssec +multi

# Query via Pi-hole (port 53)
dig @127.0.0.1 example.com +dnssec +multi

# Test DNSSEC validation
dig @127.0.0.1 -p 5335 dnssec-failed.org A +dnssec
# Expect SERVFAIL (validation works)

Troubleshooting Tips

Reference

Based on tutorial: Crosstalk Solutions — The World’s Greatest Pi-hole + Unbound Tutorial (2023) .


© 2025 Kubernix — Homelab Documentation. Prepared for Raspberry Pi 4 and Pi-hole + Unbound setup with focus on privacy and performance.