Pi-hole is open source software which provides ad blocking (and more) for your entire home network. It does this by blocking known ad serving domains. Pi-hole even has the ability to block network requests to malicious domains if the domain name is contained in one of the block lists.
The high level statistics compiled by Pi-hole provides a much greater insight to what is going on in your home network. Normally this information is either not visible or in logs buried on your router’s web interface (possibly under the advanced DNS settings). Even if your router has the logs available, it normally does not aggregate and display the data in a user-friendly format.
Pi-hole was originally designed to run on a Raspberry Pi (affiliate link), but it is currently supported on a variety of hardware platforms. I decided to give it a try on the Raspberry Pi since Pi-hole is a perfect light weight application to install on a low power single board computer. After experiencing a few issues with installing Pi-hole (which was likely my fault for not following directions), I tried running Pi-hole as a Docker image.
Installing Docker on the Raspberry Pi
Installing Docker on the Raspberry Pi is quite simple. A single command on the terminal is all you need:
curl -sSL https://get.docker.com | sh
If you wish to run docker without running the command with “sudo” then you can add the default Raspberry Pi user “pi” to the “docker” group:
sudo gpasswd -a pi docker
You will need to log out and back in or reboot your Raspberry Pi in order for the group change to occur.
Installing Portainer on Docker
To install the Pi-hole Docker image, you could follow the directions on the Pi-hole GitHub or DockerHub pages to create a script that can be executed to run Pi-hole in Docker. I took that approach at first, but I encountered a few issues. The biggest issue is that I pulled down the wrong Docker image since I was following directions that was not specific to the Raspberry Pi. For the Raspberry Pi, you must use the image: pihole/pihole:4.1_armhf. I incorrectly used pihole/pihole:latest which will not work on ARM processors like the Raspberry Pi.
Instead of creating a script, I thought I would check out Portainer so I could use Docker from a graphical web interface. I know this probably makes the command line geeks cringe. However, I think it can be useful for beginners or maybe if you just want a simple way to visualize your Docker configurations. I personally like having the option to use either command line or GUI.
To install Portainer is simple with the following commands:
docker volume create portainer_data
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data --restart always portainer/portainer
You may want to tweak the above command to set the restart policy of choice. If you want Portainer running all the time, using “always” or “unless-stopped” may be your best options. If you do not set this option, you will need to run the second command above in order to start Portainer again if you had to restart your Raspberry Pi to update it, for instance.
To check if Portainer is running:
You should see something like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3ec764b0b2de portainer/portainer "/portainer" 5 seconds ago Up 3 seconds 0.0.0.0:9000->9000/tcp frosty_kirch
If your Raspberry Pi is located at 192.168.1.6, for instance, then go to http://192.168.1.6:9000 to go to the Portainer web administration page. The first time you open portainer you will need to set up an admin user:
After you log in, you will need to select “Local” and click “Connect” since you are connecting to the local Docker installation:
Setting up Pi-hole using Portainer
In the left nagivation panel, click on “Containers”. Then click the “Add Container” button. Once there, enter a name for the new Pi-hole container. I simply entered “Pihole” and then you must specify the Docker image. Remember we must choose the ARM image “pihole/pihole:4.1_armhf” for the Raspberry Pi.
You will also need to set up the port mappings. There is an automatic “Publish all exposed ports” button but the description says it will map all of the exposed ports in the image to random ports on the host. You do not want the ports to be random since they need to be the proper ports such as port 53 for DNS. Therefore, you must manually map the ports. The following ports need mapped: 80/TCP, 443/TCP, 53/TCP, and 53/UDP. If you plan to use the built-in DHCP server, you should also map 67/UDP.
Next we need to set up the volumes in order to preserve the configuration files when the Pi-hole image/container is updated with a new version. Click on the “Volumes” tab at the bottom of the page under “Advanced container settings”. You will need to leave the values in the “Container” field the same as I have in the image below since those locations map to locations inside the Pi-hole image. However, you may place the volumes wherever you want. I put them in my home folder of my Raspberry Pi so they are easy to access should I need to look at or modify the configuration files. Notice that I used bind mounts rather than standard volumes since that allows me to map the location to the folder I specified rather than create a volume in the Docker volumes folder.
There are some environment variables that need to be set. Go to the “Env” tab to add the following environment variables:
For the server IP, I recommend using a static IP address since the Pi-hole DNS server will be the main DNS server for your home network. You will need to set up your router's DHCP service to give out the Pi-hole DNS server to all of the clients on your network (see the article I wrote on how to configure Pi-hole DNS with an OPNsense router).
Update! As of version 4.2.2, the ServerIP environment variable is no longer required, but I still recommend setting up your Pi-hole DNS server to be a static IP address assigned by your router.
The TZ environment variable is to set your local timezone which is important for proper log file rotation. You can find a list of timezones here.
Then you enter the upstream DNS servers for “DNS1” and “DNS2”. This could be Google's DNS servers or other DNS servers of your choice. For my configuration, my upstream DNS server is my router which then uses OpenDNS as the upstream DNS servers. The reason I do that is so that I can have my router resolve local DNS names on my home network. It is convenient to refer to devices by their domain names rather than IP addresses. I use OpenDNS instead of my ISP DNS servers because I like having content filtering for my family to block potential malicious or inappropriate websites.
Update! As of version 4.1.1, if you are experiencing issues with the Pi-hole using the above configuration in Portainer, the DNS options should be set via the docker command line like so:
or if you prefer docker compose:
dns: - 127.0.0.1 - 192.168.1.1
Where the second “dns” argument is your upstream DNS provider whether local or remote. This DNS option cannot be set using Portainer and is different than the DNS1 and DNS2 environment variables. I have not noticed any issues with Pi-hole setting it up via Portainer even though I do see the warning message as described in Pi-hole's documention. Even if you start your Pi-hole Docker image via command line or docker compose, you can still manage and view the docker container in Portainer. Some of your options may be limited since the container was not created in Portainer.
Finally, you can set your web administration password for Pi-hole with the “WEBPASSWORD” environment variable.
The last thing we need to set is the restart policy. Click on the “Restart Policy” tab and then click on the “Unless stopped” button. This option means that the Pi-hole container will always restart if it happens to crash and it will also start after booting up the Raspberry Pi. The container will only stop when you manually stop it. This is what we want since it is critical that the DNS server is always running on your network. Once you switch over to using the Pi-hole DNS server, if it goes down, your network essentially goes down because network devices will not know how to resolve DNS requests. Your network traffic will not know where to go.
Now click “Deploy the container” in the middle of the page. Pi-hole should be up and running after a short while! You should eventually see that it is labeled “Healthy” when it is finished loading:
To view the admin page, go to http://192.168.1.6/admin (or whatever address your Pi-hole server is located). You should be able to see the following page (after you log in with your web admin password):
All that is left to do is configure your router to use the Pi-hole DNS server but that is the topic of another article I wrote.