Synology, Docker, Pihole and Cloudflare

A while ago, I got really sick and tired of dealing with the hardware that Telus shipped me for my residential gateway, and so a new "internal" router was added. Things were good, but then I wanted to do network-wide ad blocking (to deal with ads on streaming devices...), but found that even if I specified an additional DNS server, the router would still advertise itself as a DNS server, as well as any additional DNS server I added.

Something had to be done!

The Goal

The basis of this idea is that my Synology NAS is "probably" one of the first things I'm going to turn on, and one of the more "foundational" pieces of the network, so running network-wide services on the device is sound.

The software on the Synology isn't terribly feature rich, and certainly doesn't help me with the adblocking function that I'm looking for (as well as defining custom DNS records for the network), but PiHole does. I am also trying avoid "hacking" the Synology, and leaving it as close to factory as possible so that future upgrades don't break everything.

So, the goal is simple: Run Docker on the Synology, and run PiHole as a container.

Wiring up the basics

Synology has a Docker distribution for their devices, which was a great start. Installing this was straightforward using the usual mechanism.

Pihole has a docker image, so it was a matter of configuring this. Marius Hosing has a great walk-through of how to do this through the GUI, so that at least told me it was possible. I got this going easy enough.

The catch was how do I ensure that Pihole was kept up to date? Watchtower was a good choice, and there's no shortage of resources that discuss how to run this on a Synology (including another resource at Marius Hosting).

Issues

This all worked really great, until Watchtower updated Pihole. It downloaded the new image, shut down Pihole, replaced the image and started it back up. Cool, works as designed.. right?

As part of Pihole's startup, the image checks for - and downloads - some binaries from an apt resource.

However, there's no DNS server running on the network at this moment in time, so the container shuts down thinking there's an error. Docker on the Synology starts the container back up, but since nothing has really changed, the same issue occurs again.

This happened at about 11 PM, right when my youngest son was going to read an ebook on the tablet...

A quick fix applied, and a sleep later, it was time to resolve this mess.

Docker-Compose

I really do like Docker Compose. I like the idea of defining what services I want in a configuration file. So, how do I make sure there's a DNS resolver available to the Pihole when it starts up?

This site talks about using DNS over HTTPS from Cloudflare as the upstream DNS resolver for a Pihole, which has the added advantage of hiding your DNS queries from your ISP. This solution proposed is complete with a Docker-compose.yml file that basically solves what I'm looking for.

Add Watchtower, and we're done.

PiHole and Cloudflare DNS docker-compose.yml:

cat pihole/docker-compose.yml

 1version: "3"
 2
 3services:
 4  pihole:
 5    container_name: pihole
 6    image: pihole/pihole:latest
 7    environment:
 8      TZ: 'America/Edmonton'
 9      DNSMASQ_LISTENING: local
10      ServerIP: # IP of my Synology
11      DNS1: '127.0.0.1#5053'
12      DNS2: 'no'
13      WEB_PORT: 8080
14    volumes:
15      - '/volume1/docker/pihole/dnsmasq.d/:/etc/dnsmasq.d/'
16      - '/volume1/docker/pihole/pihole/:/etc/pihole'
17    cap_add:
18      - NET_ADMIN
19      - NET_BIND_SERVICE
20    restart: unless-stopped
21    depends_on:
22      - cloudflared
23    network_mode: host
24
25  cloudflared:
26    image: crazymax/cloudflared
27    container_name: cloudflared
28    ports:
29      - "5053:5053/udp"
30      - "49312:49312/tcp"
31    environment:
32      - "TZ=America/Edmonton"
33      - "TUNNEL_DNS_UPSTREAM=https://1.1.1.1/dns-query,https://1.0.0.1/dns-query"
34    restart: always

Watchtower docker-compose.yml:

cat watchtower/docker-compose.yml

 1version: "3"
 2
 3services:
 4  watchtower:
 5    container_name: watchtower
 6    restart: unless-stopped
 7    image: containrrr/watchtower
 8    volumes:
 9      - /var/run/docker.sock:/var/run/docker.sock
10    environment:
11      - TZ=America/Edmonton
12      - WATCHTOWER_SCHEDULE=0 0 23 * * *
13      - WATCHTOWER_CLEANUP=true
14
15      - WATCHTOWER_NOTIFICATIONS=email
16      - WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG=Hostname
17      - WATCHTOWER_NOTIFICATION_EMAIL_FROM=# Valid sender
18      - WATCHTOWER_NOTIFICATION_EMAIL_TO=# Valid Recipient
19      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=in-v3.mailjet.com
20      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
21      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=# Mailjet username
22      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=# Mailjet Password
23      - WATCHTOWER_NOTIFICATION_EMAIL_DELAY=5

In this case, I am using Mailjet as my SMTP host to send me notifications from Watchtower when it does stuff.

Final Gotcha

When testing that I was actually using Secure DNS and DNSSEC from Cloudflare's check tool, I would see inconsistent results. Sometimes I would have secure DNS, sometimes not. This stemmed from an issue within Pihole, where it had Google's DNS selected as the upstream DNS servers even though the DNS servers were defined as part of the environment variables.

Updating the DNS Servers configuration to not select a "stock" upstream DNS server, and instead leaving the local DoH resolver selected seemed to be the fix I needed:

Synology DNS Settings

and gave me green results.