Why I Worked on This
I run Pi-hole on a Proxmox LXC container as my primary DNS resolver and ad blocker. It works well for my local network, but when I added Tailscale with MagicDNS to access my home network remotely, I hit a DNS conflict I hadn’t anticipated.
Tailscale’s MagicDNS assigns names to devices on the VPN using its own internal DNS (like hostname.tailnet-name.ts.net). The problem: when I’m connected to Tailscale remotely, I want to resolve my internal services by their local names (like proxmox.home or synology.local), but Pi-hole doesn’t know how to handle split-horizon DNS where the same hostname might resolve differently depending on whether you’re inside the local network or connected via Tailscale.
This isn’t a theoretical edge case. It’s a real friction point when you’re trying to access your NAS, home automation, or self-hosted apps from outside your network without remembering IP addresses or maintaining duplicate DNS records.
My Real Setup
Here’s what I’m running:
- Pi-hole 6.2.2 (FTL 6.3.3) on Debian 12 LXC in Proxmox
- Tailscale installed on the same LXC container, acting as a subnet router
- Local network:
10.1.0.0/16 - Tailscale network:
100.64.0.0/10(CGNAT range) - Internal DNS domain:
home.local(I know.localconflicts with mDNS, but that’s what I inherited from my router setup)
When I’m on my local network, Pi-hole resolves hostnames using my router’s DHCP-assigned names. When I’m remote on Tailscale, I want those same hostnames to resolve to Tailscale IPs instead of local IPs, because the local IPs aren’t routable over the VPN.
The Initial Problem
I tried using Pi-hole’s Conditional Forwarding feature, thinking I could route queries from Tailscale clients differently than local clients. I configured rules like:
true,100.64.0.0/10,100.100.100.100#53,home.local
This didn’t work. After reading Pi-hole’s documentation and some forum threads, I realized I had misunderstood what Conditional Forwarding does. It’s not about routing queries from specific client IP ranges. It’s about forwarding reverse DNS lookups (PTR records) for specific IP ranges to authoritative DNS servers.
In other words, Conditional Forwarding tells Pi-hole: “When someone asks for the hostname of 10.1.5.20, forward that reverse lookup to 10.1.1.1.” It doesn’t split forward lookups based on where the client is connecting from.
What Worked (and Why)
I ended up solving this by using Tailscale’s MagicDNS as the primary resolver for Tailscale clients, with Pi-hole as a fallback for ad blocking and local overrides. Here’s how I configured it:
Step 1: Enable MagicDNS in Tailscale
In the Tailscale admin console, I enabled MagicDNS and added my Pi-hole’s Tailscale IP (100.x.x.x) as a global nameserver. This tells Tailscale clients to use Pi-hole for DNS queries, but MagicDNS still handles .ts.net names internally.
Step 2: Add Local DNS Records in Pi-hole
For services I access remotely, I manually added DNS records in Pi-hole’s Local DNS settings, mapping hostnames to their Tailscale IPs:
proxmox.home 100.x.x.10
synology.home 100.x.x.15
homeassistant.home 100.x.x.20
This works because Pi-hole serves these records to all clients, whether they’re local or remote. When I’m on my local network, the Tailscale IPs are still reachable (Tailscale routes them correctly). When I’m remote, they resolve to the VPN addresses I need.
Step 3: Disable Conditional Forwarding
I removed all Conditional Forwarding rules. They weren’t solving my problem and were adding unnecessary complexity. Pi-hole now forwards all upstream queries to Cloudflare (1.1.1.1) for public domains and serves local overrides from its own records.
Why This Works
Tailscale’s MagicDNS is designed to coexist with custom DNS servers. It intercepts queries for .ts.net domains before they reach Pi-hole, so there’s no conflict. For everything else, Pi-hole handles ad blocking and local name resolution. The manual DNS records ensure that my internal services resolve to Tailscale IPs regardless of where I’m connecting from.
What Didn’t Work
Conditional Forwarding for Client-Based Routing
I wasted time trying to use Conditional Forwarding to route queries from Tailscale clients (100.64.0.0/10) to a different upstream DNS server. This is not what the feature does. It only handles reverse DNS for specific IP ranges, not forward lookups based on client source.
Split-Horizon DNS with dnsmasq Alone
I briefly considered writing custom dnsmasq config files to implement true split-horizon DNS, where the same hostname resolves to different IPs depending on the client’s network. While dnsmasq supports this with --server directives, it would require maintaining duplicate records and complex routing logic. I decided it wasn’t worth the maintenance burden for my setup.
Using Pi-hole’s DHCP for Tailscale
I thought about running Pi-hole’s DHCP server and assigning Tailscale clients custom DNS settings that way. But Tailscale clients don’t use DHCP—they get their DNS settings from the Tailscale control plane. This approach was a dead end.
Key Takeaways
- Conditional Forwarding is for reverse DNS, not client-based routing. If you need different DNS behavior for different clients, you need a different tool or approach.
- MagicDNS and Pi-hole can coexist if you let MagicDNS handle
.ts.netnames and Pi-hole handle everything else. - Manual DNS records work fine for small setups. I only have about a dozen services I access remotely, so maintaining a short list in Pi-hole’s Local DNS is manageable.
- Avoid
.localdomains if you can. I inherited this from my router and haven’t changed it yet, but it conflicts with mDNS and causes occasional headaches. Use a proper internal TLD like.home.arpaor a subdomain of a real domain you own. - Test with
digandpihole tail. When troubleshooting DNS, I ran queries from different clients and watched Pi-hole’s live logs to see what was actually happening. This saved me from guessing.
Limitations and Trade-offs
This solution works for my use case, but it has limitations:
- I’m manually maintaining DNS records for Tailscale IPs. If a device’s Tailscale IP changes (rare, but possible), I have to update the record.
- This doesn’t implement true split-horizon DNS. Local clients resolve to Tailscale IPs even when they could use local IPs. In practice, this doesn’t matter because Tailscale routes traffic efficiently, but it’s not optimal.
- If you have a large number of services or frequently changing infrastructure, this approach doesn’t scale. You’d need a proper DNS server with split-horizon support or dynamic updates.
For my home lab, where I have a stable set of services and don’t mind a few manual DNS entries, this setup has been running reliably for months. If I add more services or start managing DNS for other people, I’ll revisit it. For now, it works.