Install Pi-hole on a Raspberry Pi with Docker and Portainer

Have you decided to install Pi-hole on a Raspberry Pi?

Introduction

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:

docker ps

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:

Setup admin user in Portainer

After you log in, you will need to select “Local” and click “Connect” since you are connecting to the local Docker installation:

Connect to local 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. Enter “pihole/pihole:latest” as the image name.

In a prior version of this how-to, the “latest” image did not have a build for ARM platforms like the Raspberry Pi. This meant you had to manually update to each new version by changing the image name and redeploying the container. You had to choose the ARM version of Pi-hole which had a suffix of “_armhf”. Those images are still available if you need a specific older version.

By default, the “bridge” network mode is used. You do not need to change it for a basic Pi-hole setup. With bridge mode you will need to set up the port mappings between the host system and the Docker container running Pi-hole. You will notice 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. Add the following ports: 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.

Installing Pi-hole container

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.

Installing Pi-hole container

For the network options, the default is bridge mode as you will see in the text box beside the “Network” label. You may enter a hostname for the device if you like. It will not show up on your network but internally to the Docker container. Basically, the only time you will see the hostname is at the top of the Pi-hole dashboard page.

The most important settings are the DNS server settings. As of version 4.1.1, you need to specify the DNS server for the Docker container itself and localhost (127.0.0.1) must be set as the primary DNS server. The secondary DNS server can be any other DNS server you prefer. I have my secondary DNS server set to the DNS server on my router.

Installing Pi-hole container

Note: You must be using a recent version of Portainer since setting the DNS servers of the container is a long desired feature that they have finally implemented.

There are some environment variables that need to be set. Go to the “Env” tab to add the following environment variables:

Installing Pi-hole container

The TZ environment variable is used to set your local timezone which is important for proper log file rotation. You can find a list of timezones here.

To set the upstream DNS servers, add the “DNS1” and “DNS2” environment variables. 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 other upstream DNS servers. The reason I do that is so that I can have my router resolve local DNS names on my home network. If you only have a single DNS server, enter “no” for DNS2 environment variable.

If you do not specify the “DNS1” or “DNS2” environment variable(s), Pi-hole will default to Google's DNS servers of 8.8.8.8 and 8.8.4.4. Also, if you only want to use 1 upstream DNS server such as your router's DNS, if you do not put “no” in the “DNS2” variable, it will default to Google's DNS of 8.8.4.4 as your secondary DNS server. Pi-hole likes to have two upstream DNS servers unless you tell it you only want one. If you are using your router's DNS, it is ok to not have 2 servers. If your router is down, your network is going to be down anyway. A secondary DNS server is nice as a fallback server and also for load balancing DNS queries.

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.

Installing Pi-hole container

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:

Installing Pi-hole container

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):

Installing Pi-hole container

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.