This article is about the PiHole Linux software with DNS-over-TLS in Docker. PiHole is a network-level advertisement and Internet tracker blocking application. It acts as a DNS sinkhole, ideally suited for use on a private network. Docker has added support for the Raspberry Pi and hence all ARM processors.

I recently came across the DNS-over-TLS (DoT) Docker Image of qdm12 repository (https://github.com/qdm12/cloudflare-dns-server) at GitHub. A DNS “proxy” who accepts DNS requests and send them encrypted to Google (, CloudFlare polls ( or other DoT servers.

This could be deployed easily on my ODROID XU4 and Raspberry Pi 3 (two devices so that even if a device fails, the Internet at home still works). Quickly changing the DNS server on the “Fritzbox” (famous german series of routers) to the two IPs of the devices and the DNS queries of my home network were encrypted and no longer plain text through my line. While I busied myself with the topic of DNS, I stumbled upon PiHole ( Docker Image). A DNS server that answers DNS queries, unless they go to advertising servers. Thus, one can implement a low-level adware and tracking blocker, even for devices that can not install an AdBlocker.

Both combined PiHole with DNS-over-TLS, yields, through the deployment of two devices, a fail-safe, encrypted, ad-free and tracking free DNS setup for the whole family. In this post I would like to briefly explain how to build this. This tutorial is aimed both at my core audience, fans of the ODROID XU4, and Raspberry Pi users because the setup is identical.

Install Docker

Installing Docker and Docker-Compose is easy. Opens an SSH connection and type the following commands:

$ sudo su
# cd ~

curl -fsSL https://get.docker.com -o get-docker.sh # Docker repositories import of Docker, Inc.
sh get-docker.sh # Import repositories

apt install docker-ce python3-pip # install Docker and pip3

systemctl enable docker-ce # start Docker at startup
systemctl start docker-ce # start Docker now

pip3 install docker-compose # install Docker Compose
Docker Compose is responsible for the orchestration of several Docker containers and simplifies this greatly.

install PiHole and DoT proxy

If we have Docker and Docker Compose installed, we can already embark on our task. We will start a PiHole and two DNS-over-TLS server. I have directed the DoT server on different DNS upstream servers, so that one sends requests to Cloudflare and the other to Google Public DNS. Both have an availability of between 99% and 99.99%. As long as no DNS resolver can guarantee an uptime of 100%, I would recommend to everyone to always use two providers. The Docker image from qdm12 offers, besides the two already mentioned servers, also support for quad9, quadrant and cleanbrowsing. Otherwise, you can dig through entrypoint.sh yourself and add your own providers there.

So firstly we clone the Git repository of the DoT server and set this one up. If that works, we will integrate PiHole through the docker-compose.yml.

apt install git

cd ~ && git clone https://github.com/qdm12/cloudflare-dns-server.git ./dns-server

cd dns-server
In the docker-compose.yml you can adjust the settings. Among other things, I would advise to set BLOCK_MALICIOUS to off, as this blocks too much for my taste. VERBOSITY I have set to 0. And for providers, as I said before, you have the opportunity to choose between google, cloudflare, quad9, cleanbrowsing and quadrant. A closer look on the setting options can be found on the README site of the Git repo. Then you can build and start the compose-stack with docker-compose up –build #optional -d. If changes to the config files have been made, I would always start the stack with –build. Otherwise docker-compose up -d is enough to boot it up and docker-compose down to shut it down. How can we now test whether the DNS server is working or not? For this we have the small tool dig:
apt install dig

dig blaumedia.com @
the command should then return something like:
; << >> DiG 9.11.3-1ubuntu1.3 Ubuntu << >> blaumedia.com @
;; global options: + cmd
;; Got answer:
;; - >> HEADER << - opcode: QUERY, status: NOERROR, id: 42751 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDO SECTION: ; EDNS: version: 0, flags :; udp: 4096 ;; QUESTION SECTION: ; Blaumedia.com. IN A ;; ANSWER SECTION: blaumedia.com. 86400 IN A ;; Query time: 100 msec ;; SERVER: # 53 ( ;; WHEN: Wed Feb. 13 18:14:59 CET 2019 ;; MSG SIZE rcvd: 58
Did you manage so far? Very well! Then we turn one DoT server into two and add PiHole. For simplicity, firstly I goning to show you my docker-compose.yml file and will then explain what I was doing exactly. My docker-compose.yml File:
version: '2' services: cloudflare-dns-tls: build. restart: always image: qmcgaw / cloudflare-dns-server container_name: cloudflare-dns-tls environment: - VERBOSITY = 0 - VERBOSITY_DETAILS = 0 - BLOCK_MALICIOUS = off - LISTENING PORT = 53 - PROVIDER = cloudflare Networks: dnsbridge: ipv4_address: google-dns-tls: build. restart: always image: qmcgaw / cloudflare-dns-server container_name: google-dns-tls environment: - VERBOSITY = 0 - VERBOSITY_DETAILS = 0 - BLOCK_MALICIOUS = off - LISTENING PORT = 53 - PROVIDER = google Networks: dnsbridge: ipv4_address: pihole: image: pihole / pihole: 4.2.1 dns: - - ports: - "53: 53 / tcp" - "53: 53 / udp" - "80: 80 / tcp" - "443: 443 / tcp" cap_add: - NET_ADMIN Networks: dnsbridge: ipv4_address: environment: ServerIP: DEVICE_IP (eg TZ: Europe / Berlin DNS1: DNS2: WebPassword: PIHOLE_PASSWORD volumes: - '/ etc / pihole /: / etc / pihole /' - '/etc/dnsmasq.d/:/etc/dnsmasq.d/' restart: always Networks: dnsbridge: driver: bridge ipam: config: - subnet: gateway:
There are many changes, so I recommend to simply overwrite your docker-compose.yml file with mine. First of all, you should configure on line 47 the IP of your device that will run PiHole. With ip route get | awk ‘{print $ NF; exit}’ you can quickly find out which one this is (Source: AskUbuntu.com ). Under Line 51 then specify the password for the Web interface of PiHole. What are we doing here exactly? To make the two DoT servers reachable for PiHole, we create a Network Bridge at the bottom of the file. This ensures that we can assign the individual containers fixed IP addresses. Docker normally handles this so that the containers can always be reached by the container name among themselves. But DNS servers such as dnsmasq of PiHole don’t know upstream servers with their domain names not but expect IP addresses. How could he resolve these domains anyway? So give the containers fixed IPs; easy. This we determine at line 49 and 50 as the upstream server for PiHole. On line 34 and 35 we set the DNS servers of the container itself, which are for example used during the boot of the processes. The first must always be, the second can be anything else. I set here the IP of my Fritzbox (router). In line 53 and 54 we mount the Config folder of PiHole on our host system so that they are not deleted on each build process and remain persistent. That’s it so far, the rest you should already know or is self-evident. With docker-compose up –build -d we can now build and start everything. Insert PiHole Ad Channels The web interface of PiHole we reach with our browser by simply calling the IP of the Raspberry Pi / oDroids. [ Figure 02 ] PiHole WebUI Following the link you come to the interface. On the left side select “Login” in the navigation and enter the password that you have just defined in the configuration. Voilà! Best test again now that DNS queries are working with dig. If everything goes perfectly, we can now add lists for PiHole which are responsible for blocking advertisements. I get my lists usually exclusively from firebog.net . Adding them into PiHole can be done under Settings -> Blocklists.

PiHole adding blocklists

Since more people than I use in the internet in my household, I rather lock less than too many DNS addresses. Namely, if somewhere something doesn’t work due to the DNS black holes, it’s annoying for everyone. Of course, in the end this is but left to everyone theirself. At the end click on save and update and the DNS queries included in the lists are blocked. The lists are updated automatically at night at 00:00.

In the end

You now have a PiHole server that gets its DNS requests encrypted. If you are sure that everything works, you can now specify your PiHole server as a DNS server in your router. We have a “FritzBox” here and the configuration looks like this:

Set FritzBox DNS server

Enter the internal IP address of your PiHole device and save the settings. As you can see, I have entered two IPs there. In the first place the ODROID XU4, second comes my backup PI3. Tada! From now on the DNS queries of the entire network running filtered through PiHole -> DNS-over-TLS -> Cloudflare / Google / Quad9 / …


https://blaumedia.com/blog/odroid-raspberry-pi-pihole-dns-over-tls-docker/ https://github.com/qdm12/cloudflare-dns-server

Be the first to comment

Leave a Reply