Tech Expert & Vibe Coder

With 14+ years of experience, I specialize in self-hosting, AI automation, and Vibe Coding – building applications using AI-powered tools like Google Antigravity, Dyad, and Cline. From homelabs to enterprise solutions.

Debugging DNS leaks in WireGuard split-tunnel configurations when running Pi-hole with conditional forwarding to upstream resolvers

Why I Started Debugging This

I run WireGuard on my GT-AX6000 router to access my home network remotely. My setup uses Pi-hole for DNS filtering, and I wanted remote clients to use it just like local devices do. The split-tunnel configuration worked fine for most traffic, but I noticed DNS queries weren't behaving consistently. Some domains resolved through Pi-hole, others timed out, and I couldn't reach internal hostnames I'd configured in Pi-hole's local DNS records.

This wasn't theoretical troubleshooting—I was literally unable to access services I host at home when connected remotely, which defeated the entire point of the VPN.

My Actual Configuration

Here's what I was running:

  • GT-AX6000 with Merlin 3004.388.8_4
  • WireGuard server configured with "Allow DNS: Yes" and NAT disabled for IPv6
  • IP range: 10.12.0.0/24 for VPN clients
  • Pi-hole running on 192.168.0.10 in my LAN
  • Router's WAN DNS pointed to Pi-hole (192.168.0.10)
  • WireGuard client configs with DNS set to 10.12.0.1 (the router's WireGuard interface)

Local devices on my LAN could query either the router (192.168.0.1) or Pi-hole (192.168.0.10) directly and everything worked. Pi-hole served both external DNS and local hostname overrides I'd configured for internal services.

What Actually Happened

When connected via WireGuard:

  • Public domains resolved fine (like nu.nl)
  • Local hostnames known to the router's dnsmasq resolved (like hostname.local)
  • Custom domains I'd added to Pi-hole (like www.mydomain.com pointing to internal IPs) timed out completely

I verified with nslookup that queries were reaching 10.12.0.1, and I could see in Pi-hole's logs that all three types of queries arrived and were answered. The router was simply not forwarding Pi-hole's responses back to the WireGuard client for those custom local domains.

What I Learned About the Problem

The issue came down to how the router handles DNS forwarding when the upstream resolver is on the LAN side. The router's dnsmasq was forwarding queries to Pi-hole correctly, but when Pi-hole returned answers for domains with internal IP addresses that weren't in dnsmasq's own cache, something in the packet routing or firewall rules was dropping the responses before they reached the WireGuard interface.

This is distinct from the Windows DNS leak problem I'd read about (where Windows ignores interface metrics and sends queries to all available DNS servers). That's a client-side issue. My problem was server-side—the router itself wasn't completing the DNS transaction.

Why This Configuration Was Wrong

After digging into it, I realized I'd violated a basic principle: pointing the router's WAN DNS settings to an internal device creates a circular dependency during boot. The router needs DNS to set its system time via NTP, and if that DNS server is on the LAN side and the LAN isn't fully initialized yet, things break in subtle ways.

More importantly, the router's DNS forwarding behavior assumes upstream resolvers are external. When you point it at an internal resolver that returns LAN IP addresses, the routing table doesn't know how to handle the response path back through the VPN tunnel.

What Actually Fixed It

I made two changes:

  1. Moved Pi-hole out of WAN DNS settings
    Set the router's WAN DNS to external resolvers (1.1.1.1 and 8.8.8.8). This ensures the router can always reach DNS during boot and system operations.
  2. Configured Pi-hole as LAN DNS
    In LAN > DHCP Server > DNS Server, I set Pi-hole's IP (192.168.0.10) as DNS Server 1. This makes local clients use Pi-hole by default, but the router itself doesn't depend on it.

For WireGuard clients, I changed the client config DNS setting from 10.12.0.1 to 192.168.0.10 directly. This bypasses the router's DNS forwarding entirely and sends queries straight to Pi-hole over the tunnel.

The DNS Director Consideration

I also looked at DNS Director (a Merlin feature that forces all DNS traffic through specified servers). I set it to "Router" mode but added an exception for Pi-hole's IP so it could still reach its upstream resolvers (Cloudflare in my case). Without this exception, Pi-hole itself would be blocked from resolving external domains.

This wasn't strictly necessary for fixing the WireGuard issue, but it prevented other devices on the network from bypassing Pi-hole by hardcoding external DNS servers.

What Didn't Work

Before I figured this out, I tried:

  • Adjusting interface metrics — Irrelevant since this was a server-side routing issue, not a client-side leak
  • Enabling NAT for IPv6 in WireGuard — Made no difference; the problem was IPv4 DNS forwarding
  • Changing MTU settings — Unrelated to the DNS forwarding logic
  • Adding firewall rules manually — I considered this but didn't need to once I fixed the upstream DNS configuration

I also considered using Diversion (Merlin's built-in ad-blocking) instead of Pi-hole, but I prefer Pi-hole's interface and already have custom blocklists configured there. The solution needed to work with Pi-hole, not replace it.

Lessons from This Debugging Session

Never point your router's WAN DNS to a LAN device. It creates boot dependency issues and breaks assumptions about packet routing. The router should always have a path to external DNS that doesn't depend on internal services being up.

Split-tunnel VPNs need explicit DNS routing. When you're only tunneling specific traffic, DNS queries need to know exactly where to go. Relying on the router to forward them introduces unnecessary complexity.

Packet captures are essential. I used tcpdump on the Pi-hole and Wireshark on the client to confirm queries were arriving and responses were being sent. Without that visibility, I would have assumed Pi-hole was broken when it was actually the router dropping packets.

Test all three DNS scenarios separately: external domains, router-known local hostnames, and custom local DNS records. They follow different code paths and can fail independently.

Current State

WireGuard clients now resolve everything correctly: public domains, internal hostnames, and custom DNS entries in Pi-hole. The router doesn't depend on Pi-hole for its own operation, which means the network stays stable even if Pi-hole goes offline.

The only trade-off is that WireGuard clients must be able to route to 192.168.0.10 over the tunnel. If I ever change Pi-hole's IP or move it to a different subnet, I'll need to update all client configs. A small price for a working setup.