This blog post will detail how to create a custom pihole container with a pre-loaded script to update our block and allow lists automatically.
To have this custom container we will have to create our own custom docker image.
First let's create our custom
# Use the official pihole image as a base. FROM pihole/pihole:latest # Install custom software. RUN apt-get update && apt-get install -Vy php-cli php-sqlite3 php-intl php-curl python3 vim # Install the scripts to update our pihole automatically. RUN wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | bash
Dockerfile above uses the base pihole image as base and a few custom software are added. I have installed
vim to help with future automation and manipulation of config files inside the container.
Let's create our image :
root@home-svr1:~/docker-projects/pihole# docker build --no-cache --pull --network=macvlan10 -t "pihole:Dockerfile" . Sending build context to Docker daemon 30.46MB Step 1/3 : 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/3 : RUN apt update && apt install -Vy php-cli php-sqlite3 php-intl php-curl python3 vim ---> Running in 53c8cf3d6772 ---> 5c1a0664a6ad Step 3/3 : RUN wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | bash ---> Running in 1e83d9d23a6e Removing intermediate container 1e83d9d23a6e ---> 45d9d94fd232 Successfully built 45d9d94fd232 Successfully tagged pihole:Dockerfile root@home-svr1:~/docker-projects/pihole# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE pihole Dockerfile 45d9d94fd232 15 minutes ago 432MB pihole/pihole latest eb777ee00e0c 6 weeks ago 333MB
I have struggled badly to have the image connecting to the internet and pulling the updates. After hours of research, I have finally stumbled upon this article and with the help of the docker's documentation, the image has finally been created.
We need to point a working network to the docker build command for RUN to work.
We can now create our docker container configuration file :
root@home-svr1:~/docker-projects/pihole# touch docker-compose.yml
And, add the below configuration to it :
Two options that are interesting to be aware of are :
image: This option tells the docker-compose which image to use and in this case, we are point to our newly created image.
network: In the service section we defined the name of the network to be used as long with the IP address we want our container to use and in the global configuration, we used the
external flag to tell docker-compose to use an already created network.
dns: This option had to be added otherwise my container was trying to use the macvlan10 gateway as its DNS server. It seems that if not specified docker is going to use the host DNS server instead of the
Before running docker-compose we are going to test our config for any errors with the command :
root@home-svr1:~/docker-projects/pihole# docker-compose config networks: macvlan6: external: true services: pihole: cap_add: - NET_ADMIN container_name: pihole dns: - 192.168.6.1 environment: TZ: Europe/London hostname: pihole image: pihole/pihole:latest networks: macvlan6: ipv4_address: 192.168.6.10 ports: - 53:53/tcp - 53:53/udp - 67:67/udp - 80:80/tcp - 443:443/tcp restart: unless-stopped volumes: - /root/docker-projects/pihole/etc-pihole:/etc/pihole:rw - /root/docker-projects/pihole/etc-dnsmasq.d:/etc/dnsmasq.d:rw - /root/docker-projects/pihole/etc-pihole-updatelists:/etc/pihole-updatelists:rw version: '3.0' root@home-svr1:~/docker-projects/pihole#
Our configuration passed without any errors and we are ready to create our container.
root@home-svr1:~/docker-projects/pihole# docker-compose up --detach Starting pihole ... done
We shall see our container running :
root@home-svr1:~/docker-projects/pihole# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2ec52935049b pihole:Dockerfile "/s6-init" About a minute ago Up About a minute (healthy) pihole
Let's connect to it and confirm that the update script is running as expected and change the default generated password :
root@home-svr1:~/docker-projects/pihole# docker exec -ti pihole bash root@pihole:/# pihole-updatelists Pi-hole's Lists Updater by Jack'lul https://github.com/jacklul/pihole-updatelists Opened gravity database: /etc/pihole/gravity.db (4.36 MB) No remote lists are set in the configuration - this is required for the script to do it's job! See README for instructions. Updating Pi-hole's gravity... [i] Neutrino emissions detected... [✓] Pulling blocklist source list into range [✓] Preparing new gravity database [i] Using libz compression [i] Target: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts [✓] Status: Retrieval successful [i] Received 77608 domains [✓] Storing downloaded domains in new gravity database [✓] Building tree [✓] Swapping databases [i] Number of gravity domains: 77608 (77608 unique domains) [i] Number of exact blacklisted domains: 0 [i] Number of regex blacklist filters: 0 [i] Number of exact whitelisted domains: 0 [i] Number of regex whitelist filters: 0 [✓] Cleaning up stray matter [✓] DNS service is listening [✓] UDP (IPv4) [✓] TCP (IPv4) [✓] UDP (IPv6) [✓] TCP (IPv6) [✓] Pi-hole blocking is enabled Finished with 1 error(s) in 3.35 seconds. root@pihole:/# sudo pihole -a -p Enter New Password (Blank for no password): Confirm Password: [✓] New password set root@pihole:/#
The update script is working, however, it seems that there is a small bug with it. Instead of using the mapped folder, it is using the configuration file at /etc/pihole-updatelists.conf as seen below :
root@pihole:/etc/cron.d# cat /usr/local/sbin/pihole-updatelists | grep lists.conf 'CONFIG_FILE' => '/etc/pihole-updatelists.conf',
I have changed the line as seen on the image above and a bug report is going to be opened with the developer.
As you might have noticed we now have three new folders inside our docker project directory. These folders are mapped to our pihole container and should be used to back up our configs before an upgrade and to edit the lists and configurations of the script running in our container.
root@home-svr1:~/docker-projects/pihole# ll total 28 drwxr-xr-x 5 root root 4096 Apr 2 11:24 ./ drwxr-xr-x 3 root root 4096 Mar 31 21:30 ../ -rw-r--r-- 1 root root 640 Apr 2 11:23 docker-compose.yml -rw-r--r-- 1 root root 212 Apr 2 00:08 Dockerfile drwxr-xr-x 2 root root 4096 Apr 2 11:24 etc-dnsmasq.d/ drwxrwxr-x 3 systemd-coredump systemd-coredump 4096 Apr 2 11:43 etc-pihole/ drwxr-xr-x 2 root root 4096 Apr 2 11:43 etc-pihole-updatelists/
The last step is going to be to set up our custom lists editing the file pihole-updatelists.conf inside our container mapped folder as below :
; Remote list URL containing list of adlists to import ADLISTS_URL="https://v.firebog.net/hosts/lists.php?type=tick" ; Remote list URL containing exact domains to whitelist WHITELIST_URL="https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt" ; Remote list URL containing regex rules for blacklisting REGEX_BLACKLIST_URL="https://raw.githubusercontent.com/mmotti/pihole-regex/master/regex.list" ; Comment string used to know which entries were created by the script ; You can still add your own comments to individual entries as long ; you keep this string intact COMMENT="Managed by pihole-updatelists" ; Maximum time in seconds one list download can take before giving up ; You should increase this when downloads fail because of timeout DOWNLOAD_TIMEOUT=180
I am using a conservative set of blocking rules, but you can check the external links at the end of the article for more aggressive blocking lists.
Let's apply our lists :
root@home-svr1:~/docker-projects/pihole# docker exec -ti pihole bash root@pihole:/# pihole-updatelists Pi-hole's Lists Updater by Jack'lul https://github.com/jacklul/pihole-updatelists Opened gravity database: /etc/pihole/gravity.db (4.36 MB) Fetching ADLISTS from 'https://v.firebog.net/hosts/lists.php?type=tick'... done (30 entries) Processing... 30 inserted Fetching WHITELIST from 'https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt'... done (191 entries) Processing... 191 inserted Fetching REGEX_BLACKLIST from 'https://raw.githubusercontent.com/mmotti/pihole-regex/master/regex.list'... done (14 entries) Processing... 14 inserted Updating Pi-hole's gravity... [✓] Storing downloaded domains in new gravity database [✓] Building tree [✓] Swapping databases [i] Number of gravity domains: 373804 (271031 unique domains) [i] Number of exact blacklisted domains: 0 [i] Number of regex blacklist filters: 14 [i] Number of exact whitelisted domains: 191 [i] Number of regex whitelist filters: 0 [✓] Cleaning up stray matter [✓] DNS service is listening [✓] UDP (IPv4) [✓] TCP (IPv4) [✓] UDP (IPv6) [✓] TCP (IPv6) [✓] Pi-hole blocking is enabled Finished successfully in 24.22 seconds.
Before the conclusion of our set up let's change the upstream DNS servers pihole is using to resolve the queries. It comes set with google by default, but I prefer to use the more privacy-focused quad9 servers.
Although our pihole is serving DNS to our network if you are not "hijacking" the DNS traffic and forwarding it to your pihole hosts inside your network can bypass your pihole and go to the internet to query its servers.
Let's imagine our smart TV trying to query a domain to serve ads and it failing with your network's DNS then a hardcoded DNS server is queried instead like google and since your network is not redirecting DNS traffic to your pihole the ad is going to be served and of course that's not the behaviour that we expect from the devices inside our network, for this reason in a future article we are going to be forwarding all DNS traffic to our pihole preventing hosts bypassing it.
At this point, your pihole should be serving DNS to your network like mine is. I hope that my guide helps you with your set up also I would like to thank my colleague Geraldo for pointing me in the right direction when selecting blocking lists.
Below there are a set of handful links used as the source for this article :