Every internet transaction starts with a DNS lookup. Whether it is a browser requesting a website, a mobile app sending diagnostic telemetry, or a smart TV phoning home viewing statistics, domain name resolution is the front door of your network.
Commercial upstream resolvers (Google DNS, Cloudflare, ISP DNS) log domain requests and retain usage metadata. By pairing Pi-hole (for ad and tracker sinkholing) with Unbound (for local recursive root zone resolution), you eliminate upstream telemetry and protect your entire network without browser extensions.
High-Availability Architecture (Primary + Secondary)
Running a single DNS server creates a single point of failure. If that node reboots or crashes, internet connectivity halts for all connected devices.
┌───────────────────────────────┐
│ Client Request │
└───────┬───────────────┬───────┘
│ │
Primary DNS ▼ ▼ Secondary DNS
┌─────────────────┐ ┌─────────────────┐
│ Pi-hole #1 │ │ Pi-hole #2 │
│ (10.10.10.5) │ │ (10.10.10.6) │
└────────┬────────┘ └────────┬────────┘
│ Gravity-Sync │
│ ◄───────────────────► │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Unbound #1 │ │ Unbound #2 │
│ (127.0.0.1:5335)│ │ (127.0.0.1:5335)│
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
▼
Root DNS Servers (.) & TLDsWe deploy two Raspberry Pi or lightweight Alpine Linux VM nodes:
10.10.10.510.10.10.6Configuring Unbound as a Recursive Resolver
Rather than forwarding blocked requests to third-party providers, Unbound queries the 13 root name servers directly, traversing Top-Level Domain (TLD) servers until it receives an authoritative answer.
Install and configure Unbound (/etc/unbound/unbound.conf.d/pi-hole.conf):
server:
verbosity: 1
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
# Use root hints file
root-hints: "/var/lib/unbound/root.hints"
# Trust anchor file for DNSSEC validation
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Buffer and cache tuning
so-rcvbuf: 4m
so-sndbuf: 4m
msg-cache-size: 64m
rrset-cache-size: 128m
infra-cache-numhosts: 10000
# Privacy protections
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 2Start the service and verify DNSSEC validation:
sudo systemctl enable --now unbound
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335 # Must return SERVFAIL
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335 # Must return NOERRORSynchronizing Pi-hole Nodes with Gravity-Sync
When you whitelist a domain or add blocklists on your primary Pi-hole, having to manually replicate those changes on the backup node is error-prone. Gravity-Sync automates database synchronization over SSH.
curl -sSL https://gravity.vmstan.com | bashgravity-sync setup on the primary node. It will exchange SSH keys, link local SQLite databases (gravity.db and custom.list), and establish a 30-minute systemd synchronization timer.Configure your router's DHCP options to broadcast both 10.10.10.5 and 10.10.10.6 as DNS servers. Your network now enjoys redundant, zero-telemetry, ad-sinkholed name resolution.