Pi-hole deployed at the edge on Fly.io and accessed via TailScale
Update: this setup was made obsolete by Tailscale integrating natively with NextDNS.
- Pi-hole is usable as a DNS resolver by trusted laptops and mobile devices
- Pi-hole is deployed at the edge, for low latency while traveling anywhere
- Pi-hole is NOT accessible from the public internet, to avoid making it available to bad actors
- Pi-hole is accessible from trusted devices only, via wireguard, courtesy of TailScale
- Deploy and configure the app on Fly.io:
fly launch --no-deploy
, including:- Y - Copy the toml configuration app to the new app
- enter - Leave the app name blank or provide your own
- enter - Pick the region closest to your location
- enter - Use the provided Dockerfile to build an image
- Set the password on Pi-Hole's web interface via the
WEBPASSWORD
environment variable:fly secrets set WEBPASSWORD="<password>"
- Generate an ephemeral and reusable auth key in tailscale's admin portal
- Store the auth key as a secret in fly.io:
fly secrets set TAILSCALE_AUTHKEY="tskey-<key>"
- Deploy to fly.io:
fly deploy
- (optional) Scale the VM to a dedicated CPU and 2 GB of RAM:
fly scale vm dedicated-cpu-1x
- (optional) Troubleshoot the Pi-hole configuration via its web interface:
<tailscale IP>/admin
- Open the DNS configuration page
- Add nameserver > custom > enter the TailScale private IP for the Pi-hole
- Turn on
Override local DNS
- Download and install tailscale on the device, then authenticate
- There's no step 2! 🤯
- The Pi-hole currently starts before TailScale, resulting in a warning message in the Pi-hole diagnosis page that
interface tailscale0 does not currently exist
. Still, the Pi-hole properly resolves queries via its TailScale private IP, so this is not a real issue, merely a bit messy. - When Fly rolls out a new version, it relies on a rolling or blue-green deployment approach by default, which means that Tailscale will display more than one machine for a certain period of time. Thankfully, being "ephemeral", they're cleaned up after some amount of time being inactive.
- Configuring a backup public DNS nameserver in Tailscale breaks the setup as TailScale seems to respond with whatever DNS resolver is faster, rather than trying them in order. This turns out to be a pretty big issue if the Pi-hole goes offline, as DNS resolution fails completely across the network. I've opened a feature request but this is a pretty niche use case so don't hold your breath. Workarounds include:
- Configure DNS Resolution on each device with Pi-hole as primary and public DNS resolvers as backup
- Disconnect a device entirely from TailScale when DNS misbehaves, so as to revert to its default DNS configuration.
- Scale to two Fly.io regions for redundancy and add both TailScale IPs as DNS nameservers. This only works if relying on the essentially stateless OOTB setup for Pi-hole.; It also breaks unified reporting.
- Redeploying or upgrading Pi-hole leads to a new Fly.io instance, with a new TailScale private IP, thus requiring an update to the DNS configuration. This is rare enough for me as to be a non-issue, but it might be quite annoying for very frequent travelers. As a workaround,
mdeeks
points out on HN that it should be possible to persist the "machine key" stored intailscaled.state
across restarts.
- What's a less messy way to keep Docker running after it executes its
CMD
than runningtail -f /dev/null
? I've looked into using Fly's processes but I don't think it buys me much. - Is there a need to disable TailScale key expiry for the Pi-hole machine? Probably not, given that the auth key can be reused.
- Deploy Pi-hole in a home lab
- Use NextDNS instead