Posted: December 31, 2021
I run several of my local network services (such as git and wiki) as Docker containers on one host. A Traefik proxy multiplexes the services on this host. I wanted to add Pi-hole here.
There is a readily available, supported Pi-hole Docker image. That worked great for me.
I use Docker Compose to manage my Docker containers. I went looking for a docker-compose.yml file example on the net, but all the ones I found seemed unnecessarily complicated.
So, I thought I’d share my simple solution. Here it is:
services: traefik: . . . pihole: image: pihole/pihole:latest ports: # port 80 will go through Traefik - "53:53/tcp" - "53:53/udp" environment: - "TZ=America/Chicago" - "WEBPASSWORD=xxxxxxxx" - "VIRTUAL_HOST=pihole.chez-rufus.lan" labels: - "traefik.enable=true" - "traefik.http.routers.pihole.rule=Host(`pihole.chez-rufus.lan`, `pihole`)" - "traefik.http.services.pihole.loadbalancer.server.port=80" volumes: - pihole_etc:/etc/pihole restart: unless-stopped volumes: pihole_etc: name: pihole_etc
Pi-hole provdes three network services: a DNS resolver, a DHCP server, and a web server.
I’m exposing the DNS resolver (network ports 53/tcp and 53/udp) directly on the Docker host. I’ve seen a lot of solutions that run those through the Traefik proxy. That’s unnecessary.
I am leaving the DHCP server (network port 67/udp) disabled, which is why you don’t see any configuration for it above. A lot of the solutions I saw run this service but that too usually is unnecessary. Your gateway/router already has a DHCP server. If it allows you to configure the DNS assignments then there isn’t any reason not to use that.
That leaves the web service (network port 80/tcp), which provides the administration screen. I do run that through the Traefik proxy.
The DNS setup in my gateway router has a static entry for “pihole.chez-rufus. lan”:
$ host pihole.chez-rufus.lan pihole.chez-rufus.lan has address 192.168.20.6
It’s an alias for the host that has all my Docker containers.
The VIRTUAL_HOST environment setting is defined as this host. So, when
a local user visits http://pihole.chez-rufus.lan/ they’ll get the Pi-hole administrative screen.
Pi-hole DNS Configuration
I made a few adjustments to the default Pi-hole DNS configuration, as shown to the right (click to enlarge).
The default Pi-hole configuration uses Google Public DNS as the upstream DNS server. I don’t want that, so I unchecked the boxes normally set.
I added the gateway router (192.168.20.1) as the upstream DNS server. That way queries that aren’t blocked by the Pi-hole are passed upstream for resolution.
Finally, I removed the check on two options: “Never forward non-FQDN queries” and “Never forward reverse lookups for private IP ranges”. I want to forward those queries on to the gateway router for resolution.
With this setup, DNS requests are processed as follows:
- First, by the Pi-hole, to filter out blocked sites
- Next, by the gateway router, to resolve local resources on the network
- Finally, my router passes the query to its upstream DNS resolver (I use Google Public DNS) to resolve public Internet addresses.
The final change was to modify the DHCP configuration in my gateway router so network hosts were told to use the Pi-hole for DNS resolution, rather than the router itself.
With the setup complete, DNS request processing happens as shown in this sequence diagram:
My solution does require that DNS queries make one additional stop at the gateway router, for local resource resolution. That could be avoided by running DHCP on the Pi-hole. I didn’t want to do that. My gateway router has significantly higher uptime expectations than my Docker server, so I’d rather run DHCP there. The added overhead of this decision is small and worth the increased reliability.
The presented docker-compose.yml is a minimal solution for running Pi-Hole as a Docker container behind a Traefik proxy.