Use Pi-hole with Tailscale
Now that Tailscale Services work (previous post) and I can give a Pi-hole service easily a name in my Tailscale network, I actually installed a Pi-hole. The initial setup was easy. The tricky part was IPv6.
Running Pi-hole
I am running Pi-hole with all my containers (immich, forgejo, homeassistant, ...) on an 8GB Raspberry PI 4 compute module. The initial Docker compose setup is based on the version in the Pi-hole documentation.
I want my Pi-hole to have IPv6, so I added a /etc/docker/daemon.json with this contents:
And after a lot of experimentation with Docker networks this is my working compose.yml:
services: pihole: container_name: pihole image: pihole/pihole:latest ports: - "53:53/tcp" - "53:53/udp" # local port 80 is already used - "8314:80/tcp" environment: TZ: 'Europe/Berlin' FTLCONF_webserver_api_password: 'insert-a-secret-password-here' FTLCONF_dns_listeningMode: 'ALL' volumes: # persisting Pi-hole's databases and common configuration file - './etc-pihole:/etc/pihole' restart: unless-stopped # for IPv6 cap_add: - NET_ADMIN networks: pihole-net: ipv6_address: fd42:cafe:dead:beef::53 networks: pihole-net: driver: bridge enable_ipv6: true ipam: config: - subnet: fd42:cafe:dead:beef::/64
A differences to the default setup is that I changed the http port to 8314 (8+pi), because I already use port 80 for something else.
Because of Tailscale services the container will get an address: https://pihole.tail07efb.ts.net.
I configured this only for port 443 and NOT for port 53, because Tailscale Services don't support UDP.
Because I could not reuse the IPv6 address of the Docker bridge, so I use a new Unique local address subnet that is not used anywhere else on this host.
The IP addresses on the System Settings page of my Pi-hole looks like this now:

As far as I understand IPv6, fe80:: address will not work to route IPv6 with Docker, so I ignored it.
That it is shown here should be no issue.
ArchLinux setup
My workplace computer is running ArchLinux and obviously I want to use Pi-hole here.
I switched to using systemd-resolved as described in the Tailscale Knowledgebase.
My workplace system is not using NetworkManager, but pure systemd-networkd instead.
I still needed to restart that because of DHCP interference, the same way I would have done for NetworkManager.
My /etc/systemd/resolved.conf.d/pi-hole.conf looks like this:
The first entry is the local IPv4 address of my container hosting Raspberry PI and the commented DNS line is the IPv6 of the Tailscale address of the same host.
I decided to use the local address of the Pi-hole host and not their Tailscale address, because I switch occasionally to the work Tailscale network.
When looking at the generated /etc/resolv.conf, switching to another Tailscale network would remove the routing to the first entry (and therefore disable DNS filtering).
However, since the system would use the next nameserver listed in the file (the one provided by my router), DNS resolution would still function properly.
On my travel notebook, which is running ArchLinux too, I used the Tailscale address of the container Raspberry PI. Here I use NetworkManager, because it is easier to connect to a new Wi-Fi. The uplink at home shouldn't be a bottleneck here. I will monitor how annoying the latency to my home network is.
Raspberry PI setup
My movie player is a Raspberry PI 5 with Rasperry PI OS.
First I needed to install systemd-resolved:
Then I edited the /etc/systemd/resolved.conf and added my local DNS server
This PC will never move, so using the local IP is sensible.
Conclusion
Only a few percent of the queries are actually filtered.
But I use in my browser already adblockers, so that was expected.
For the IPv6 usage I looked a the query types after a few hours and about half the queries are IPv6 ones (AAAA):

Of course I tested the Pi-hole with other DNS queries, like TXT, MX and NS. Everything works, including IPv6. I would call this a success.



