Since Docker added support for the Raspberry Pi, many other ARM boards got supported as well. The ODROID-XU4 is predestined for Docker because it has much more power and RAM than a Raspberry Pi. My first project, that I wrote about  some weeks ago on my blog, was PiHole in combination with two DNS-over-TLS tunnels to Cloudflare and Google DNS.

But what is that? PiHole is a DNS blackholing tool that blocks requests to specific domains so your end-devices can’t even load the IP addresses of the supposed ad servers to connect. On a PC, it’s not even closely effective as an adblocker, but what about mobile apps, smart tvs or other devices where you can’t install an adblocker? PiHole has a great community and many help tutorials.

And what is DNS-over-TLS / DoT? The DNS protocol itself is quite old and, at the time when it was written, nobody cared about encryption. So, every time you visit a website, you basically send an unencrypted request to a DNS server that could be read by anyone between you and the DNS server. Doesn’t sound good right? DNS-over-TLS puts a TLS-Encryption-Layer onto your request so only you and the DNS server know what domain you’re requesting.

In this tutorial, I would like to show you how to install PiHole and two DNS-over-TLS tunnels to the Google DNS and Cloudflare DNS within Docker to easily administrate it.

Install Docker

The installation of docker and docker-compose is very simple. Open up an SSH connection and paste in the following:

$ 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 needed to orchestrate multiple docker containers that belong together. It simplifies the whole management.

Install PiHole and DoT-Proxy

After installing docker and docker-compose, we can continue with the interesting parts. Both DNS servers, Google and Cloudflare, have an uptime of almost 99% to 99.99% percent. To prevent my network being down due an downtime of one provider, I’ve decided to choose multiple different providers. To setup the DoT-tunnel, I’ve found a great Docker image from qdm12 on github (https://github.com/qdm12/cloudflare-dns-server) that works flawless for many months now. The image supports also the DNS servers of Quad9, Quadrant and CleanBrowsing. Otherwise, you can edit the entrypoint.sh file and add your own DoT-providers. So, to install and start it, paste the following commands into a BASH terminal window:

$ apt install git
$ cd ~ && git clone https://github.com/qdm12/cloudflare-dns-server.git ./dns-server
$ cd dns-server
Now open the docker-compose.yml file and change the settings. I recommend setting the BLOCK_MALICIOUS option to off, because, in my opinion, it blocks too much, and I’ve experienced several false positives with it. You can add blocklists for malicious pages later in PiHole. To see the other possible settings, you can have a look at the github readme at https://github.com/qdm12/cloudflare-dns-server#environment-variables.

When you’ve finished, you can start it with the command “docker-compose up --build” (or “docker-compose up --build --d” to run it in the background). If you’ve changed the docker-compose.yml file, I always recommend building the compose stack again with --build. Otherwise a “docker-compose up --d” to start and “docker-compose down” to stop is enough. Just be sure to run the command in the directory of the project. Next, let’s test that the server is working:

$ apt install dig
$ dig blaumedia.com @
The output should look like the following:
; <<>> 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

; EDNS: version: 0, flags:; udp: 4096
;blaumedia.com.            IN    A

blaumedia.com.        86400    IN    A

;; Query time: 100 msec
;; WHEN: Wed Feb 13 18:14:59 CET 2019
;; MSG SIZE  rcvd: 58
It’s working? Nice! Now let’s add another DoT-proxy and PiHole. First, I would like to show you my docker-compose.yml file and afterwards explain it. You can just copy and paste it into your docker-compose file.
version: '2'
    build: .
    restart: always
    image: qmcgaw/cloudflare-dns-server
    container_name: cloudflare-dns-tls
      - VERBOSITY=0
      - PROVIDER=cloudflare
    build: .
    restart: always
    image: qmcgaw/cloudflare-dns-server
    container_name: google-dns-tls
      - VERBOSITY=0
      - PROVIDER=google
    image: pihole/pihole:4.2.1
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
      - "443:443/tcp"
      - NET_ADMIN
      ServerIP: DEVICE_IP ( f.e.)
      TZ: Europe/Berlin
      - '/etc/pihole/:/etc/pihole/'
      - '/etc/dnsmasq.d/:/etc/dnsmasq.d/'
    restart: always
    driver: bridge
       - subnet:
At the beginning, you should add the IP address of your device in line 47, which you can find it out easily with the shell command “ip route get | awk '{print $NF; exit}'”. Afterwards, set a password in line 51 for the pihole web interface.

But what are we doing exactly? To let the two DoT-proxies be accessible by PiHole, we have to give them static IP addresses. We’re adding a network bridge at the bottom of the file to accomplish this. We set the IPs of the proxies in line 49 and 50 as upstream servers for PiHole. In line 34 and 35, we set the upstream servers for the PiHole container itself. The first has to be, and the second should be a normal DNS server, which will be used for starting the container. I have chosen the IP address of my network router.

In lines 53 and 54, we mount the config directories of PiHole out of the container, so it’ll be persistent and won’t get deleted on every new build process of the container. If you’ve finished everything, you can start the compose stack again with a “docker-compose up --d --build” command.

Adding ad lists to Pihole

To open the web interface, you just have to put the IP address of your ODROID into your browser, as shown in Figure 1.

Now click the “Did you mean to go to the admin panel?” and click on “Login” to continue to the next page. Put your configured PiHole password and voilà, you’re in! You should check the DNS connectivity again with dig and if everything works correctly, you can continue with adding blocklists. I got my lists from https://firebog.net/, and insert them into PiHole via the “Settings” => “Blocklists” menu, as shown in Figure 2.

Now you have an encrypted, ad-blocking DNS server. When you’ve checked that everything works, you can set the DNS server of your network router to the ODROID so that every device in your network uses your PiHole . In my case, I have a Fritzbox, which looks like Figure 3.

I have 2 devices with PiHole to have a backup service if I’m updating one. Just paste your IP address there, and from then on, your network devices get ad-filtered DNS responses.

Do you have any questions or experience any problems? You can contact me in the comment section on the original German post at https://blaumedia.com/blog/odroid-raspberry-pi-pihole-dns-over-tls-docker/.

Be the first to comment

Leave a Reply