I’m a big fan of Pi-hole. Pi-hole is a piece of software that runs on a Raspberry Pi or other computer and provides ad-blocking and privacy-protecting functionality by providing DNS resolution to your computers.

I’ve run into the same problem twice with Pi-hole, which is why this post exists: I will show you how to route queries for specific hosts to specific resolvers, overriding Pi-hole’s default behavior - but only for specific (sub) domains.

The setup:

  • A VPN connection between a local network and a remote network where the remote network uses an internal resolver for DNS.
  • Tailscale-connected clients between a local network and various remote networks where MagicDNS is enabled to facilitate internal resolution for all Tailscale nodes.

Because I need to send queries for certain domains to other nameservers - some outside the network, some within the network - I’ll use a bit of functionality within dnsmasq: the actual DNS resolver application that Pi-hole uses. What I’m looking to do is tell the resolver to send queries for a specific set of hostnames to a specific nameserver - one that’s neither configured within Pi-hole nor used as a default resolver.

To set this up, head to /etc/dnsmasq.d/ and create a new configuration file. I’ve called mine 02-overrides.conf. You don’t want to change 01-pihole.conf because that file will be overridden if you make changes to Pi-hole’s configuration or when Pi-hole is upgraded. Within this configuration, define the domains or sub-domains you want to override and where to send them. Mine looks like this:

server=/somedomain.io/10.240.0.1
server=/internal/10.240.0.1
server=/a.somedomain.net/10.240.0.1

Once the configuration file is saved, you’ll need to restart Pi-hole’s resolver by navigating to Pi-hole’s web UI, clicking on “Settings” and then “Restart Pi-hole resolver”

Try it out!

Before:

$ host web.somedomain.io
Host web.somedomain.io not found: 3(NXDOMAIN)

After:

$ host web.somedomain.io
web.somedomain.io has address 10.210.1.31
web.somedomain.io has address 10.210.1.21
web.somedomain.io has address 10.210.1.11

As you can see, the first query fails because the default upstream DNS resolver can’t find it, and that’s expected. Once we add the new configuration, queries go to the internal nameserver that does know what that host resolves to, and you get an answer.

Bonus round: Tailscale’s MagicDNS

I use Tailscale’s MagicDNS functionality so that all connected clients receive a DNS name for easy reference. Their implementation is pretty slick: when you opt to use it, the local Tailscale service will run a DNS resolver at 100.100.100.100 and will provide answers for beta.tailscale.net. Because my local computer can be a mess with a variety of DNS settings and networks, I decided to shift Tailscale MagicDNS resolution to the same host that runs Pi-hole. It, too, runs Tailscale - so it has the clever local resolver at 100.100.100.100.

Thus, I simply added beta.tailscale.net to the override list, sending queries for that domain to the local resolver:

server=/beta.tailscale.net/100.100.100.100

…and it’s good to go!

$ host moomin.domain.tld.beta.tailscale.net
moomin.domain.tld.beta.tailscale.net has address 100.101.191.65