Introduction
Pi-hole:
- DNS resolver that returns
NXDOMAINfor ad/tracking domains - Covers all apps: mobile, smart TV, console, IoT
- Community blocklists (StevenBlack, OISD)
- Web dashboard with per-client stats
- Manual whitelist / blacklist
- API and CLI
- Optional DHCP integration
vs Unbound: Pi-hole is blocking + UI focused; Unbound is recursive resolution focused. Often combined: Pi-hole in front (UI + blocking), Unbound behind (private recursion).
Prerequisites
- Linux VPS Debian / Ubuntu (or Raspberry Pi)
- 512 MB RAM, 1 vCPU min
- Root access
- Port 53 UDP/TCP free
- Port 80 (UI) free
Step 1: Installation
curl -sSL https://install.pi-hole.net | bash
Interactive script asks:
- Network interface
- Upstream DNS provider (Quad9, Cloudflare)
- Default blocklists
- Web dashboard (yes)
- Query logging (yes)
- Privacy level (Show everything)
At end:
Configure your devices to use the Pi-hole as their DNS server using:
IPv4: 192.168.1.10
Your Admin Webpage login password is: aBc123XyZ
Step 2: UI access
Visit http://VPS_IP/admin. Login with displayed password.
Step 3: Configure your devices
3 options:
- Per-device manual: change DNS in each device.
- Via router: change LAN DNS in your router. All DHCP clients automatically get Pi-hole.
- Pi-hole as DHCP: Pi-hole can be your DHCP server (Settings > DHCP).
Step 4: First test
From a Pi-hole client:
dig doubleclick.net
Should return 0.0.0.0 or NXDOMAIN.
Dashboard shows live:
- Total queries
- Queries blocked
- % blocked
- Top clients
- Top blocked domains
Step 5: Add blocklists
Group Management > Adlists. Recommended:
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
https://big.oisd.nl
https://adaway.org/hosts.txt
https://v.firebog.net/hosts/Easyprivacy.txt
https://v.firebog.net/hosts/AdguardDNS.txt
After adding: Tools > Update Gravity to compile.
Step 6: Whitelist and blacklist
Group Management > Domains > Whitelist > add example.com.
Group Management > Domains > Blacklist > add adparasite.com.
Regex supported:
(\.|^)telemetry\..*\.microsoft\.com$
Step 7: Combine with Unbound (local recursive)
Pi-hole forwards to Cloudflare/Quad9 by default. For more privacy, add Unbound behind:
- Install Unbound (see tutorial 30) on same machine, port 5335:
server:
interface: 127.0.0.1
port: 5335
- In Pi-hole, Settings > DNS:
- Uncheck all upstream DNS
- Custom 1 (IPv4):
127.0.0.1#5335
Pi-hole filters, Unbound resolves recursively without external middleman. Max privacy.
Step 8: Groups (multi-tenant)
Different policies per device/group:
Group Management > Groups > create "Adults" and "Kids".
Group Management > Clients > add IP/MAC, assign to group.
Group Management > Adlists > assign some lists to "Kids" group.
Step 9: DNS over HTTPS for upstream
sudo apt install -y cloudflared
sudo nano /etc/default/cloudflared
CLOUDFLARED_OPTS=--port 5053 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
Systemd:
sudo nano /etc/systemd/system/cloudflared.service
[Unit]
Description=cloudflared DNS over HTTPS proxy
After=syslog.target network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/default/cloudflared
ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_OPTS
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now cloudflared
In Pi-hole > Settings > DNS > Custom 1: 127.0.0.1#5053.
Step 10: Auto-update blocklists
Default: weekly. To force more often:
sudo crontab -e
0 3 * * * /usr/local/bin/pihole updateGravity > /var/log/pihole-gravity.log 2>&1
Step 11: Backup and restore
Settings > Teleporter > Backup downloads .tar.gz with full config.
To restore: Teleporter > Restore > select file.
Step 12: API
curl http://VPS_IP/admin/api.php?summary&auth=YOUR_TOKEN
Token in Settings > API.
Useful endpoints:
summary: global statstop_clients: most active clientsgetQueryTypes: query types breakdowntopItems: top domains queried/blocked
Troubleshooting
"Reply UNKNOWN_HOST"
Pi-hole doesn't resolve:
sudo systemctl status pihole-FTL
sudo tail -f /var/log/pihole/pihole.log
dig @1.1.1.1 google.com
Legit domain blocked by mistake
Query Log > find domain > "Blocked by". Whitelist it.
Dashboard inaccessible
sudo systemctl status lighttpd
sudo systemctl restart lighttpd pihole-FTL
Check port 80 not used by Nginx/Apache.
Pi-hole + Unbound: SERVFAIL
If Pi-hole forwards to 127.0.0.1#5335 but Unbound not running:
sudo systemctl status unbound
sudo unbound-checkconf
Update fails
pihole -up
sudo tail -f /tmp/pihole_update.log
Useful commands
pihole status
pihole -t # live tail
pihole -up # update Pi-hole
pihole -g # update gravity (blocklists)
pihole disable
pihole disable 10m
pihole enable
pihole -w example.com
pihole -b adparasite.com
pihole --regex '^.*ads.*'
pihole -c # chronometer
pihole -a -p # reset admin password
sudo tail -f /var/log/pihole/pihole.log
sudo tail -f /var/log/pihole/FTL.log
Conclusion
Pi-hole turns your network into ad/tracker-free zone:
- Blocks ads at DNS level (all apps)
- Real-time per-client stats
- Combinable with Unbound for max privacy
- Very light (runs on Raspberry Pi)
Going further:
- Combine with Unbound for recursive resolution
- Enable Pi-hole DHCP for auto client naming
- For HA, deploy 2 Pi-hole + Gravity Sync
Resources
- Official docs: https://docs.pi-hole.net
- Site: https://pi-hole.net
- GitHub: https://github.com/pi-hole/pi-hole
- Gravity Sync: https://github.com/vmstan/gravity-sync

















