What you need:
Raspberry Pi
which will host Pi-hole later:Raspberry Pi 1 Model B
Raspberry Pi 1 Model B+
Raspberry Pi 2 Model B
Raspberry Pi 3 Model A+
(no ethernet, only wifi)Raspberry Pi 3 Model B
Raspberry Pi 3 Model B+
Raspberry Pi 4 Model B
Raspberry Pi 400
Raspberry Pi 5
- High-quality power supply
SD card
orMicro SD card
(depending on Raspberry Pi model) with a minimum size of4GB
- (Micro) SD card reader
- Running Operating System (Linux preferably) which is used to setup Pi-hole on the Raspberry Pi
- Ethernet cable (optional) because this guide presumes a wired connection to setup and operate Pi-hole
- Access to router on local network with permission to change DHCP settings
No display or keyboard has to be attached to the Raspberry Pi because this guide performs a headless installation. But attaching both is still useful for diagnosing problems.
Other Raspberry Pi's, such as Raspberry Pi Zero
, might also be compatible to Docker Pi-hole, but are likely to require
extra configuration steps due to missing ethernet or wifi interfaces.
One single core systems, such as Raspberry Pi Zero
, Raspberry Pi 1 Model B
and Raspberry Pi 1 Model B+
,
performance might be better on native Pi-hole installs, without Docker's multi-tasking.
NOTE: Script rpi_pi-hole_assembler.sh automates most of steps in this chapter: It
downloads and prepares a Raspberry PI OS image with Docker Pi-hole. Simply edit the script in your favorite editor and
customize all configuration settings marked with TODO
. Afterwards run it and flash the resulting image to a (Micro) SD
card, e.g. using Raspberry Pi Imager
. Finally, jump to the
Router Setup chapter.
First download and extract Raspberry Pi OS Lite
, then flash
it to the (Micro) SD card using e.g. Raspberry Pi Imager
.
This guide was tested with Raspberry Pi OS Lite
which was released on March 15th 2024
and is based on Debian 12
(Bookworm).
Next step is to perform the network setup of Raspberry Pi. For an interactive setup (requires display and keyboard) plug the SD card into the Raspberry Pi, power on the system and follow the official guides to configure a static (!) ip address for ethernet or wifi.
For a headless setup of the network, plug the SD card into the host OS, mount the second partition (rootfs
) of the
SD card and edit the files for ethernet or wifi directly.
For example, to set static IPv4 and IPv6 addresses for Raspberry Pi's ethernet port create a new file
/etc/NetworkManager/system-connections/first.nmconnection
:
# 2024 Jakob Meng, <jakobmeng@web.de>
#
# Network configuration
#
# This example assigns a static ip address, similar to
# $> nmcli con add con-name "first" ifname eth0 type ethernet ip4 192.168.0.2/16 gw4 192.168.0.1
# $> nmcli con mod "first" ipv4.dns "1.1.1.1"
# $> nmcli con mod "first" ipv6.address "fd00::192:168:0:2/128"
# $> nmcli con mod "first" ipv6.dns "2606:4700:4700::1111"
# $> nmcli con up "first"
#
# Ref.: https://www.networkmanager.dev/docs/api/latest/nm-settings-nmcli.html
[connection]
id=first
type=ethernet
interface-name=eth0
[ethernet]
[ipv4]
address1=192.168.0.2/16,192.168.0.1
dns=1.1.1.1;
method=manual
[ipv6]
addr-gen-mode=default
address1=fd00::192:168:0:2/128
dns=2606:4700:4700::1111;
method=auto
Enable remote access using SSH. For a
headless setup, plug the SD card into the host OS, mount the first partition (boot
) of the SD card, cd
into the
mounted directory and touch ssh
(sshswitch.service
will then enable SSH and remove this file on the next boot).
Check for existing SSH keys
and if you do not already have an SSH key then generate a new SSH key and add it to ssh-agent
.
Plug the SD card into the Raspberry Pi, power on the system and login with SSH.
The default username is pi
with password raspberry
.
Copy your SSH public key to the Raspberry Pi.
The following steps will all be executed via SSH on the Raspberry Pi:
# Become root.
sudo -s
# Disable logins except ssh (optional).
passwd --lock pi
# Disable password authentication for SSH logins.
#
# NOTE: Ensure SSH public key authentication works before disable password authentication!
#
cat << 'EOF' > "/etc/ssh/sshd_config.d/99-disable-password-authentication.conf"
# 2024 Jakob Meng, <jakobmeng@web.de>
PasswordAuthentication no
EOF
# Restart ssh to apply changes.
systemctl restart ssh.service
# Disable bluetooth and wifi (optional)
# NOTE: Do not block wifi if you are using wifi instead of ethernet!
rfkill block all
# or
#rfkill block bluetooth
# Disable swap to reduce sd card wear and increase its lifespan (optional)
apt-get remove -y dphys-swapfile
# Disable persistent logging in journald to reduce sd card wear and increase its lifespan (optional)
# Ref.: /usr/share/doc/systemd/README.Debian.gz
rm -rf "/var/log/journal"
# Upgrade all installed packages
apt-get update
apt-get upgrade -y
apt-get dist-upgrade -y
# Install tools
apt-get install -y vim screen aptitude fzf git curl
# Install Docker runtime and Docker Compose
apt-get install -y docker.io docker-compose
# Change hostname using your favorite editor, e.g. Vim (optional)
vi /etc/hostname
vi /etc/hosts
# Reboot to apply changes
reboot
Login to Raspberry Pi via SSH after system has rebooted. Now we will enable unattended upgrades of Raspberry Pi OS
.
/lib/systemd/system/apt-daily-upgrade.timer
). Interrupting the update process might cause a broken system!
If you do not want to enable unattended upgrades, skip the next paragraph.
:warning:
# Become root.
sudo -s
# Set up unattended upgrades
apt-get install -y unattended-upgrades
# Ref.: /var/lib/dpkg/info/unattended-upgrades.postinst
cp -rav /usr/share/unattended-upgrades/20auto-upgrades /etc/apt/apt.conf.d/20auto-upgrades
# Synchronize debconf database with locales' config which will help during
# package updates because debconf will not complain about config changes
dpkg-reconfigure -f noninteractive unattended-upgrades
# Enable service which delays shutdown or reboots during upgrades
systemctl is-enabled unattended-upgrades.service || systemctl enable unattended-upgrades.service
# Reboot after updates if required to apply changes
sed -i -e 's/\/\/Unattended-Upgrade::Automatic-Reboot "false";/Unattended-Upgrade::Automatic-Reboot "true";/g' \
/etc/apt/apt.conf.d/50unattended-upgrades
# Reboot to apply changes
reboot
Next we will setup Pi-hole using Pi-hole's Docker image. Docker Compose will be used to manage the Docker containers.
Login to Raspberry Pi via SSH after system has rebooted. Create a config file docker-compose.yml
in a new folder
/opt/pihole
for Docker Compose:
# Become root.
sudo -s
# Prepare Docker Pi-hole.
mkdir -p "/opt/pihole"
# Create a new config Docker Compose using your favorite editor, e.g. Vim
vi "/opt/pihole/docker-compose.yml"
Docker Pi-hole provides an example config file for Docker Compose (without Watchtower) that could be used as a start
(docker-compose.yml.example
).
The following docker-compose.yml
example configures Docker Pi-hole with host networking mode to allow DHCP responses.
See Docker DHCP and Network Modes for rationale and other networking modes.
Watchtower will be used to automate base image updates of Pi-hole's Docker
container.
watchtower:
key and its content from the Docker
Compose file docker-compose.yml
.
:warning:
version: "3"
# https://github.com/pi-hole/docker-pi-hole/blob/master/README.md
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
network_mode: "host"
# Pi-hole environment variables
# Ref.: https://github.com/pi-hole/docker-pi-hole#environment-variables
environment:
TZ: 'Europe/Berlin'
# WEBPASSWORD: 'set a secure password here or it will be random'
DNSSEC: 'true'
# IPv4 address of Raspberry Pi
FTLCONF_LOCAL_IPV4: '192.168.0.2'
# IPv6 address of Raspberry Pi
# Mandatory to block IPv6 ads
FTLCONF_LOCAL_IPV6: 'fd00::192:168:0:2'
# Enable DHCP for IPv4? Only required if your router is not
# (or cannot be) configured to announce Pi-hole as name server.
# See section on router setup below for more info.
#DHCP_ACTIVE: 'true'
#DHCP_START: '192.168.0.101' # first IPv4 address used for DHCP
#DHCP_END: '192.168.0.254' # last IPv4 address used for DHCP
#DHCP_ROUTER: '192.168.0.1' # router ip, mandatory if DHCP server is enabled
#DHCP_LEASETIME: '64'
# DHCPv6 Rapid Commit
# Ref.: https://discourse.pi-hole.net/t/option-enable-dhcp-rapid-commit-fast-address-assignment/17079
#DHCP_rapid_commit: 'true'
# Enable DHCPv6 for IPv6? Only required if your router is not
# (or cannot be) configured to announce Pi-hole as name server.
# See section on router setup below for more info.
#DHCP_IPv6: 'true'
# Increase time (in milliseconds) Pi-hole scripts in /etc/cont-finish.d can take before S6 sends a KILL signal,
# if Pi-hole's container fails to start with error messages like e.g.
# s6-supervise pihole-FTL: warning: finish script lifetime reached maximum value - sending it a SIGKILL
# s6-supervise cron: warning: finish script lifetime reached maximum value - sending it a SIGKILL
#S6_KILL_FINISH_MAXTIME: 30000
# Volumes store your data between container upgrades
volumes:
- './etc-pihole/:/etc/pihole/'
- './etc-dnsmasq.d/:/etc/dnsmasq.d/'
# run `touch ./var-log/pihole.log` first unless you like errors
# - './var-log/pihole.log:/var/log/pihole.log'
# Recommended but not required (DHCP needs NET_ADMIN)
# https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
cap_add:
- NET_ADMIN
# Autostart Docker Pi-hole at system boot
restart: unless-stopped
# Remove watchtower key and its contents if you do not want to enable Docker image updates
watchtower:
container_name: watchtower
image: containrrr/watchtower:latest
# Watchtower environment variables
# Ref.: https://containrrr.dev/watchtower/arguments/
environment:
TZ: 'Europe/Berlin'
WATCHTOWER_CLEANUP: 'true'
WATCHTOWER_INCLUDE_RESTARTING: 'true'
WATCHTOWER_ROLLING_RESTART: 'true'
WATCHTOWER_TIMEOUT: '30s'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Autostart Watchtower at system boot
restart: unless-stopped
Now bring up all containers with:
# Become root.
sudo -s
cd "/opt/pihole"
# Run container in background.
docker-compose up -d
# Verify that all containers are up and running.
docker ps
# View output of Pi-hole container.
docker logs pihole
# Get web password.
# NOTE: Keep a backup of the web password because only the printed random password of the first run will be used
# and stored as web password within Pi-hole. On later runs the random passwords will be ignored.
# Ref.: https://github.com/pi-hole/docker-pi-hole/issues/781#issuecomment-775241580
docker logs pihole | grep -i password
Open a browser and enter ip address of your Raspberry Pi. Navigate to the Pi-hole admin panel by following the link on
Did you mean to go to the admin panel?
. Enter the web password and have a look around. Now proceed with router setup.
Follow Pi-hole's official guide to setup your fritzbox so that it distributes Pi-hole as DNS server for IPv4 via DHCP.
In short, first enable Advanced
(Erweitert
in german) view on fritzbox's web interface by clicking on Standard
in the lower left corner on the welcome screen. Then enter Pi-hole's IPv4 address as Local DNS server
at Home Network
➡️ Network
➡️ Network Settings
➡️ IP Adresses
➡️ IPv4 Configuration
➡️ Home Network
. On a german user interface this option Lokaler DNS-Server
can be found at
Heimnetz
➡️ Netzwerk
➡️ Netzwerkeinstellungen
➡️IP-Adressen
➡️
IPv4-Konfiguration
➡️ Heimnetz
.
To enable Pi-hole for IPv4 clients of the guest network you will have to setup Pi-hole as the upstream DNSv4 server of
your fritzbox. Navigate to Internet
➡️ Account Information
➡️ DNS server
and enter Pi-hole's
IPv4 address as Preferred DNSv4 server
and Alternative DNSv4 server
. On a german ui this options can be found as
Bevorzugter DNSv4-Server
and Alternativer DNSv4-Server
at Internet
➡️ Zugangsdaten
➡️
DNS-Server
.
At the time of writing Pi-hole's fritzbox guide does not cover the IPv6 setup. First ensure that IPv6 support has been configured on your fritzbox, e.g. follow the official AVM guides for IPv6 on FRITZ!Box 7590 (DE) / (EN).
Then navigate to Home Network
➡️ Network
➡️ Network Settings
➡️ IP Adresses
➡️ IPv6 Configuration
➡️ Unique Local Addresses
. Tick/enable option Always assign unique local addresses (ULA)
. Here you will also find the Unique Local Address (ULA) of your fritzbox. Ensure that the first
64 bits of the Raspberry Pi's IPv6 ULA (fd00:0000:0000:0000
in /etc/dhcpcd.conf
example above) do match your
fritzbox's ULA. Scroll down to header DNSv6 Server in the Home Network
, tick/enable Also announce DNSv6 server via router advertisement (RFC 5006)
and enter the IPv6 address of your Raspberry Pi as the announced DNSv6 server.
On german fritzboxes the menu can be found at Heimnetz
➡️ Netzwerk
➡️ Netzwerkeinstellungen
➡️IP-Adressen
➡️ IPv6-Konfiguration
. Tick Unique Local Addresses (ULA) immer zuweisen
and
DNSv6-Server auch über Router Advertisement bekanntgeben (RFC 5006)
. Then enter Pi-hole's IPv6 address in Lokaler DNSv6-Server
.
To enable Pi-hole for IPv6 clients of the guest network you will have to setup Pi-hole as the upstream DNSv6 server of
your fritzbox. The steps for IPv6 are equal to the IPv4 setup above except that the IPv6 options are named Preferred DNSv6 server
and Alternative DNSv6 server
or in german Bevorzugter DNSv4-Server
and Alternativer DNSv4-Server
.
Configure your router to announce Pi-hole's IPv4 and IPv6 addresses as DNS servers on your local network, the necessary steps should be roughly equivalent to the AVM FRITZ!Box setup described above.
If your router does not allow to change the DNS server that its DHCP server announces then try to disable your router's DHCP server completely and enable Pi-hole's DHCP server instead.
If your router does not allow to change the DNS server and to disable the DHCP server, then manually set the DNS servers of your devices to the IPv4 and IPv6 addresses of your Raspberry Pi.
For help you might have a look at Pi-hole's Discourse User Forum
and
FAQ.
Take any device that is on your local network like e.g. smartphone. Disconnect it from network and (re)connect it to your network. Check which DNS name servers have been assigned to your device, e.g. follow this guide to find your DNS. It should list the ip address(es) of Pi-hole.
Navigate to adblocker test pages (example, another example) to see if Pi-hole does its job for IPv4 pages.
Test IPv6 connectivity with test-ipv6.com
.
Test if Pi-hole is used as a IPv6 DNS resolver. For example, use dig
which is provided by distribution packages that
might be named bind-tools
,
bind-utils
or dnsutils
:
# Map domain name to IPv6 address
# SERVER should be the IPv6 address of Pi-hole
dig AAAA heise.de -6
On an Apple iPhone or iPad install iSH (github, ios), reconnect to your Wifi and enter in iSH:
# install dig
akg add bind-tools
# Map domain name to IPv6 address
# SERVER should be the IPv6 address of Pi-hole
dig AAAA heise.de -6
Creative Commons Attribution Share Alike 4.0 International
(CC-BY-SA-4.0
)