Recursive DNS+AD-Blocker — Part 2: Installing Pi-hole without caching on Synology NAS with Docker

Installing Pi-hole on a Synology NAS with Docker is quite trivial, disabling caching is not, so let’s see how to do it. You will also learn how to build your own docker image that overrides default cache settings. Key info is generic so it is valuable for other Docker installations too, if you’re not running Docker on a Synology box.

Gianni Costanzi
Nerd For Tech

--

Update 18 April 2021: added link to my public giannicostanzi/pihole-nocache image on Docker Hub

Update 01 May 2021: added link to Dockerfile sources on my GitHub Page MightySlaytanic/pihole-nocache

Update 26 Jan 2023: official Pi-hole container has CUSTOM_CACHE_SIZE that allows you to set CACHE_SIZE to zero without the need of my custom image

Introduction

In the previous post Recursive DNS Resolver with AD-Blocking Features I’ve explained how to implement on a Raspberry Pi device a DNS resolver that blocks ADs and malicious sites (Pi-hole) and resolves names recursively (Unbound) without relying on official DNS servers like Google ones. As I’ve said in that post I have deployed two Pi-holes and two Unbound servers in my home network, to have a bit of redundancy when I’m doing maintenance and to have a bit of fun :) The first Pi-hole+Unbound stack was deployed on an RPi3, so I had to choose another home-device that is active 24x7x365 for the second stack: my Synology DS218+ NAS with Docker was the perfect solution. This time we will focus on Pi-hole installation, leaving Unbound for another post.

This article is about Synology Docker but the info you can find can be perfectly applied to any device on which you’re running Docker. If you’re starting to use docker on a device without a GUI as on Synology, give a look at the portainer/portainer container which can provide a Web GUI to manage Docker’s Images, Containers, Volumes etc.

I’ll assume you’ve already installed and configured Docker on Synology via Package Manager. Screenshots are in Italian, but I think it will be easy to understand the equivalent in your language by looking at the positioning of the elements on the interface.

Installing Pi-hole

First of all open Docker app, go to Registry and search for pihole. Select the pihole/pihole image, press Download and select the latest tag.

Pi-hole Image Download

Then, before starting a new container with this image, prepare the following folder structure (you can create it via File Station app):

/volume1/docker/pihole/etc-dnsmasq.d
/volume1/docker/pihole/etc-pihole
/volume1/docker/pihole/etc.pihole_advanced

Note: /volume1 is a folder you can see via SSH CLI and its the folder containing the shared folders on your NAS. I don’t remember if docker shared folder is created by Docker app installation procedure, otherwise you can create it via Synology GUI and then you can proceed creating pihole subfolders.

As you can see on the Docker Hub page of pihole/pihole, the first two folders need to be mounted within the container to grant data persistance to your configuration when you need to re-create the container to update the image. The third one is a workaround I will explain later to set CACHE_SIZE to ZERO (read the previous article to understand why we want it to be zero).

First Pi-hole run

Now you can go to the Images section of the Docker app on Synology, select pihole/pihole:latest and press Launch. The app will ask you about the initial configuration of the container. Set the name as you want (default is pihole-pihole1) and press Advanced in order to configure the advanced options. Enable automatic restart, if you want, and then move to the Volumes tab: here we will mount the first two folders, ignore the third one, by pressing Add Folder, selecting the folders you’ve created before and mounting them on the correct paths with Read/Write permissions (I usually call the folder on synology as the path on which it will be mounted with dashes instead of slashes):

Volumes’ Bind Mounts

Then move on the Port settings tab and expose the ports you want to be reachable to outside. In particular, I want to expose:

  • DNS Service on 53/UDP and 53/TCP ports
  • HTTP Service (GUI) on 8080/TCP port

I’ll expose the DNS ports on the original ones (home devices can’t point to other non standard ports for DNS resolution) while I move the HTTP port to 8080/TCP (80/TCP is used by Synology):

Note: Synology GUI show you the services that are declared in the Dockerfile manifest as being exposed by the container and by default it exposes them on automatically allocated ports. You can remove such mappings if you don’t want to expose some container’ services or you can expose them on statically known ports as above.

Go to the Environment tab (the last one) and set a variable called WEBPASSWORD to the admin password you will use to access HTTP GUI (if you don’t set it it will be randomly generated and you will be able to see it in the container logs by double clicking on the container and then on the Log tab.

Configuration is finished, apply the setup and let Synology Docker launch the newly created container.

Setting CACHE_SIZE to zero

As we’ve seen in the previous article, we need to disable caching in order to have Unbound pre-fetching and caching work as expected. I’ve tried to set CACHE_SIZE=0 via Environment variable or modifying /etc/pihole/setupVars.conf that I have on my synology shared folder (as I’ve did on RPi3) and that is mounted within the container but it didn’t work. After a bit of study I’ve understood that the initialization script /root/ph_install.sh that is automatically executed by the container configures dnsmasq (the process that is responsible for Pi-hole dns resolution and caching) by replacing the values contained within /etc/.pihole/advanced/01-pihole.conf with the values contained in setupVars.conf and then it copies that file to /etc/dnsmasq.d/01-pihole.conf . The strange thing is that it does not take CACHE_SIZE from setupVars.conf but instead it has a CACHE_SIZE=10000 harcoded within the script.

This is the content of /etc/.pihole/advanced/01-pihole.conf

# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Dnsmasq config for Pi-hole's FTLDNS
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
###############################################################################
# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
# #
# IF YOU WISH TO CHANGE THE UPSTREAM SERVERS, CHANGE THEM IN: #
# /etc/pihole/setupVars.conf #
# #
# ANY OTHER CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE #
# WITHIN /etc/dnsmasq.d/yourname.conf #
###############################################################################
addn-hosts=/etc/pihole/local.list
addn-hosts=/etc/pihole/custom.list
domain-needed
localise-queries
bogus-priv
no-resolv
server=@DNS1@
server=@DNS2@
interface=@INT@
cache-size=@CACHE_SIZE@
log-queries
log-facility=/var/log/pihole.log
local-ttl=2
log-async

You can see this file by double-clicking on the running container and then on the Terminal tab and creating a new bash session as you can see in this screenshot where you can write the highlighted cat command:

Executing “cat /etc/.pihole/advanced/01-pihole.conf” within the container

Alternatively you can run the same command via cli by connecting via SSH to the Synology NAS, after becoming root:

docker exec -it pihole-pihole1 cat /etc/.pihole/advanced/01-pihole.conf

You can save this file on your NAS, creating /volume1/docker/pihole/etc_.pihole_advanced/01-pihole.conf and modifying cache-size=@CACHE_SIZE@ to cache-size=0

Then, stop the pihole-pihole1 container, select it and click on modify and add a new Read-Only file mapping (not a folder mapping this time) in order to mount /volume1/docker/pihole/etc_.pihole_advanced/01-pihole.conf on /etc/.pihole/advanced/01-pihole.conf within the container:

Mounting 01-pihole.conf with cache-size modified

You can now restart the container and connect to the Pi-hole GUI running on http://your-nas-ip:8080/admin GUI, enter the admin password and theck on the Settings page that the DNS cache is set to zero:

Check DNS cache size, it must be ZERO if everything works as expected

Now, you can configure Pi-hole as you’ve seen in the previous article and you can point the Unbound server running on the Raspberry as on the other Pi-hole setup as an Upstream DNS. In this way we’ve doubled the Pi-hole servers, a first step toward redundancy (we still have a single Unbound server, so if RPi goes down, DNS resolution won’t work, but let’s focus on Pi-hole now and double Unbound in the next article).

You can now try the procedure you will do to update a container on Synology and verify that your settings are persisted and cache size is still zero:

  1. Shutdown the container
  2. Click on Action -> Erase (not Delete). In Italian we have Cancella and Elimina, the first one deletes the container but keeps it’s configuration (volumes’ and ports’ mappings etc) within Docker GUI, in order to let you Launch the container again, thus re-creating it from the latest image you’ve downloaded. Elimina instead deletes the container with all of its settings. You must choose the first action, because we do not want to reconfigure it again. I don’t know the exact words used in English but I think they could be Erase (Cancella) and Delete (Elimina), so try to understand the correct one (the one to avoid should be the lowest one in the menu).
  3. Go back to the Registry and search for pihole again and re-download the latest version.
  4. When the download is finished, launch the container again, it will be restarted with the newly downloaded image and with all of your settings in place
Erase the container in order to re-create it with an updated image

Note: if you launch the Pi-hole GUI you will be noticed about new versions by looking at the footer of the page. If version numbers are blinking in red, it means that there is an official update available. BTW, it could be necessary to wait some days in order to have an updated container image (you can go to the pihole/pihole Docker Hub page and check if the latest tag has been recently updated). Automatic container update can be done via containrrr/watchtower container running on Synology but this is beyond the scope of this article.

What I don’t like about overwriting 01-pihole.conf

As you’ve seen above, we’ve overwritten the content of /etc/.pihole/advanced/01-pihole.conf file with our modified one in order to force cache-size to zero. If a new version of pihole/pihole with some changes in that file comes out, we won’t use it because we mount our version of the file over the container updated file. So, I wanted to modify the CACHE_SIZE=10000 setting in ph_install.sh setup script of the container, in order to seti ti to CACHE_SIZE=0.

Building my own pihole-nocache image

The first thing you could think of is “let’s copy ph_install.sh script, modify the variable and mount that file over ph_install.sh script of the container base image”, but this would be exactly the same as overwriting 01-pihole.conf file.

So I’ve thought that I could simply do a replace of that instruction within ph_install.sh without replacing the whole file with my own copy.

UPDATE 18 April 2021: I’ve uploaded my modified image also on the public Docker Hub giannicostanzi/pihole-nocache page so you can grab it directly from ther if you don’t want to build it on your own.

UPDATE 01 May 2021: I’ve uploaded the Dockerfile of my pihole-nocache Docker Image on my MightySlaytanic GitHub page

DISCLAIMER: the following instructions require SSH access to the Synology NAS with the ability to become root, so you must be the NAS administrator. Understand the commands before trying them on a production environment. There should be no risk but I’m not responsible for bricking your Synology Docker environment.

First of all I’ve created the following folder:

mkdir /volume1/docker/_IMAGES/pihole-nocache

Then I’ve created a Dockerfile file within that folder that instructs docker build to create a new image based on the official pihole/pihole:latest with my modification done via sed command:

# cat /volume1/docker/_IMAGES/pihole-nocache/Dockerfile
FROM pihole/pihole:latest
RUN sed -i -e "s:CACHE_SIZE=[0-9]\+:CACHE_SIZE=0:g" /root/ph_install.sh

So, we replace whatever value CACHE_SIZE has been set to in the ph_install.sh script with ZERO.

Then you can build your image with the following command:

# docker build --pull /volume1/docker/_IMAGES/pihole-nocache/ -t pihole-nocache:latest
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM pihole/pihole:latest
latest: Pulling from pihole/pihole
Digest: sha256:3a39992f3e0879a4705d87d0b059513af0749e6ea2579744653fe54ceae360a0
Status: Image is up to date for pihole/pihole:latest
---> eb777ee00e0c
Step 2/2 : RUN sed -i -e "s:CACHE_SIZE=[0-9]\+:CACHE_SIZE=0:g" /root/ph_install.sh
---> Running in 4305759e9f70
Removing intermediate container 4305759e9f70
---> ffb77613b225
Successfully built ffb77613b225
Successfully tagged pihole-nocache:latest

After doing this, you will find pihole-nocache image in your images section of the Docker app on Synology and you will be able to create a new container based on it by following the steps you’ve done before for pihole/pihole image. If you’ve deleted pihole-pihole1 container you can re-create a new one with the same volumes and port mappings, except for the /etc/.pihole/advanced/01-pihole.conf file which is no more necessary. Let’s start the new container and check that cache is set to zero without mounting the 01-pihole.conf file as expected.

Updating the container requires you to re-launch the docker build command (the — pull option forces the build process to search for an updated version of the base image) instead of performing the download described in step 3. in the update procedure described before.

Uploading the image on your private container

As I’ve said before, I have a Watchtower container running on Synology NAS that regularly updates my containers by pulling new images and re-creating them automatically. In order to allow it to update my pihole-nocache image, I’ve created a private registry and each night I rebuild the pihole-nocache image with the command explained above and upload it to my registry.

First of all I’ve created a registry container with the following command:

Creating and starting a private Registry container called registry

Note: in my home network I have a local resolution for the public name my-nas-fqdn.synology.me of my NAS, in order to resolve it on the private IP address of my Synology box. In this way the Synology certificate and key I’m mounting in the registry container will work fine because they match the FQDN name I’m using to point the container. More detail about deploying a private regitry in plain HTTP can be found on the official documentation at this link.

You can then test if the registry is listening on port 55000/TCP via the following command (CTRL^C to terminate execution) that should show you the public certificate of your NAS:

openssl s_client -connect my-nas-fqdn.synology.me:55000

You can add this registry in the Images section of the Docker app and make it active with my-nas-fqdn name, for example, By cliccing on Add and then Add from URL and entering https://my-nas-fqdn.synology.me:55000 as URL.

This allows you to search images in your private registry. Just remember to re-activate the default one when you need to search for official images.

Now, we must build the image tagging it in order to let docker know it is an image that will be found on our private registry and not on Docker hub by changing the -t option in the following way:

docker build --pull /volume1/docker/_IMAGES/pihole-nocache/ -t my-nas-fqdn.synology.me:55000/pihole-nocache:latest

If you go to the Images section of Docker Hub you will see that the registry of the newly created image is my-nas-fqdn instead of Docker Hub.

We can then upload the image to our registry (here my image was already up-to-date on the registry):

# docker push my-nas-fqdn.synology.me:55000/pihole-nocache:latest
The push refers to repository [my-nas-fqdn:55000/pihole-nocache]
b6d5ed12d029: Layer already exists
e996f48635d0: Layer already exists
010e0146144d: Layer already exists
ef63252a9627: Layer already exists
8eeede195441: Layer already exists
d3088e548a33: Layer already exists
8ac01c752962: Layer already exists
4e3984d0f56b: Layer already exists
3d64ad524bb4: Layer already exists
56e1f33806ae: Layer already exists
0990c4f0eff1: Layer already exists
b546331c0c35: Layer already exists
ea63252b317e: Layer already exists
9eb82f04c782: Layer already exists
latest: digest: sha256:b36cd790060f9f3fc20a5909b722a2f31a1c9c24ed235f03e533884dda3a244e size: 3244

Now if you do a search on your private registry from Docker app on Synology you will find pihole-nocache image, ad you will be able to download its latest tag.

Enabling automatic re-build and upload of pihole-nocache on my private registry

Finally, in order to let Watchtower find an updated version of pihole-nocache on my private registry, I’ve scheduled on my NAS the following script to run every day and that will re-create and re-upload with the latest tag all the images described by a Dockerfile within each subfolder in /volume1/docker_IMAGES folder:

docker_images_rebuild.sh

When you create a new image with the latest tag, the previous one’s tag becomes <none>. It can’t be removed until the container is updated because is still in use.

View dangling images

When it is not used anymore you can remove it via the following command:

docker rmi IMAGE_ID

replacing IMAGE_ID with the ID shown by docker image ls.

I’ve scheduled a weekly job that removes dangling images in order to save space (it also shows you how to send notifications to a user within Synology web GUI via synodsmnotify tool):

Today I’ve updated the Image and the daily run of Watchtower upgraded it and deleted the dangling old image (I use soulassassin85/docker-telegram-notifier container to send notifications to a private Telegram channel)

Telegram notification sent by Watchtower

Conclusions

I hope you’ve enjoyed reading this post, maybe it can save you a bit of time if you want to solve problems like disabling the cache on a dockerized Pi-hole or if you are in the process of learning Docker and you want to experiment a bit like me. If you have comments feel free to write me below, I’ve started learning docker two weeks ago, so there can be something that could be done better or in a more efficient way :)

And now, let’s move on with Unbound on Synology NAS: Recursive DNS+AD-Blocker — Part 3: Installing Unbound with DNSSEC on Synology NAS with Docker

Update 10 May 2021: In the article 4 of this series I’ll show you how I’ve implemented a monitoring script that collects metrics from Pi-hole servers and uploads to an InfluxDB2 server to allow me plotting useful info on a Grafana Dashboard:

--

--

Gianni Costanzi
Nerd For Tech

Network Engineer, Music Lover, Motorbike Rider, Amateur Photographer, Nerd-inside