This document describes how to set up Pi-Hole for network-wide ad blocking. It also includes optional steps for setting up Unbound for recursive DNS resolution in case you don't want to use a third-party DNS resolver like Google or Cloudflare.
Please click the below dropdowns to access the section you need.
Pi-Hole is a popular DNS sinkhole that serves as a network-wide ad blocker. It's easy to set up and it works wonders on blocking ads and trackers on your network. It can run on very light hardware, including its namesake, the Raspberry Pi.
My installation sits in a Linux Container (LXC) on one of my Proxmox nodes. (Proxmox is the hypervisor that I use on my servers.) An LXC uses fewer resources than a full virtual machine.
With this project, I also use Unbound for recursive DNS resolution. It supports both DNS-over-TLS and DNS-over-HTTPS for increased security and privacy. This means that I am not subject to a third-party for my DNS resolution, like Cloudflare or Google. One the great things about this is that it doesn't require much more effort or configuration on top of getting Pi-Hole going. This is an optional step.
To use Pi-Hole, it is recommended that you have a supported Linux distribution (distro) running. The Pi-Hole project officially supports Raspberry Pi OS, Ubuntu, Debian, Fedora, and CentOS. You can also install Pi-Hole in a Docker container, which is not described here.
Unbound should be available in all distro repositories.
For the sake of this documentation, I will assume that you're using a Debian-based distro. (From the supported list, that would be Debian itself, Ubuntu, and Raspberry Pi OS.)
You will need, at minimum, 2GB of file space (4GB is recommended) and 512MB of RAM. If you want to have internet access with no downtime, you will need a machine that runs 24/7. This could be something as small as a single-board computer (e.g. a Raspberry Pi, Orange Pi, etc.) or a cheap mini PC.
You will also need access to your router's DNS settings if you wish to apply Pi-Hole's ad blocking to your whole network. Please be sure you know how to do this, as it is outside the scope of this to cover every router manufacturer's firmware. (Most router interfaces can be found at 192.168.1.1
in your browser.)
If you can't change your router's DNS settings, you can still use Pi-Hole for ad blocking by changing the DNS settings on your devices. I encourage you to look up how to do that for your device(s) specifically as I will not cover that here.
Below are the steps needed to get Pi-Hole installed. The full page version may be viewed here.
curl -sSL https://install.pi-hole.net | bash
eth0
or ens18
.Yes
to use the default blocklist. You can adjust this later if you choose.Yes
.Yes
to install admin interface's web server requirements.Yes
. Otherwise, select No
.http://pi.hole/admin
or http://SERVERIP/admin
.Web Interface Password:
.Below are the steps needed to get Unbound installed and configured. The full page version may be viewed here.
sudo apt install unbound
/etc/unbound/unbound.conf.d/pi-hole.conf
server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
# May be set to yes if you have IPv6 connectivity
do-ip6: no
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
# Reduce EDNS reassembly buffer size.
# IP fragmentation is unreliable on the Internet today, and can cause
# transmission failures when large DNS messages are sent via UDP. Even
# when fragmentation does work, it may not be secure; it is theoretically
# possible to spoof parts of a fragmented DNS message, without easy
# detection at the receiving end. Recently, there was an excellent study
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
# in collaboration with NLnet Labs explored DNS using real world data from the
# the RIPE Atlas probes and the researchers suggested different values for
# IPv4 and IPv6 and in different scenarios. They advise that servers should
# be configured to limit DNS messages sent over UDP to a size that will not
# trigger fragmentation on typical network links. DNS servers can switch
# from UDP to TCP when a DNS response is too big to fit in this limited
# buffer size. This value has also been suggested in DNS Flag Day 2020.
edns-buffer-size: 1232
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 1
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m
# Ensure privacy of local IP ranges
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
sudo service unbound restart
5335
, which is where the above config file told Unbound to broadcast to.)
127.0.0.1#5335
If you're using a Debian-based distro for this, you might encounter a conflict with Unbound's resolver and the system's resolvconf service. This can interfere with Pi-Hole/Unbound, so here's how to fix that issue.
systemctl is-active unbound-resolvconf.service
active
, then stop the service with the following command.
sudo systemctl disable --now unbound-resolvconf.service
sudo sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf
sudo rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf
sudo service unbound restart