Split Tunnel WireGuard: Private Services Without Routing Everything
Most WireGuard tutorials show you how to route all traffic through the tunnel. That is great for privacy but terrible for latency and bandwidth. If you just want to access a few private services on your server, there is a much better approach.
The Problem
I run several services on my VPS that should not be publicly accessible: a git server, an admin panel, a VPN management UI, and a mail server admin interface. These need to be reachable from my laptop and phone, but nobody else should be able to see them.
The obvious solution is a VPN. But I do not want all my traffic going through a server in Germany when I am watching a video or browsing the web. I want my normal internet connection for everything except the few private services.
Split Tunnel Configuration
The key is the AllowedIPs setting in the WireGuard client config. Instead of 0.0.0.0/0 which routes everything, you specify only the VPN subnet:
[Interface]
PrivateKey = <client-private-key>
Address = 10.0.0.2/24
DNS = 10.0.0.1
[Peer]
PublicKey = <server-public-key>
Endpoint = your-server-ip:51820
AllowedIPs = 10.0.0.0/24
With this config, only traffic destined for 10.0.0.x addresses goes through the tunnel. Everything else takes the normal route. Your browsing speed stays the same, your public IP does not change, and you still get access to your private services.
DNS Is Where the Magic Happens
The split tunnel routes packets, but your browser needs to know that admin.example.com should resolve to a VPN IP instead of a public one. This is where a local DNS server comes in.
I run dnsmasq on the VPN server, listening only on the WireGuard interface. It has entries like:
address=/git.example.com/10.0.0.1
address=/admin.example.com/10.0.0.1
address=/vpn.example.com/10.0.0.1
For any domain not in this list, dnsmasq forwards the query to Cloudflare or Google DNS. So private subdomains resolve to the VPN IP, and everything else resolves normally.
The client config sets DNS = 10.0.0.1 which tells the system to use the VPN DNS server. Combined with the split tunnel routing, DNS queries for private domains go through the tunnel to dnsmasq, and all other DNS queries go through the tunnel too but get forwarded to public DNS by dnsmasq.
The DNS-over-TLS Conflict
This one took me a while to figure out. I use a DPI bypass tool on my local machine that forces all DNS through DNS-over-TLS via systemd-resolved. When the VPN comes up, the DNS setting should override this, but it does not always work cleanly.
The fix was a PostUp script in the WireGuard config:
PostUp = resolvectl dnsovertls %i no
This disables DNS-over-TLS specifically on the VPN interface so queries to 10.0.0.1 go through as plain DNS over the tunnel. Without this, the system tries to establish a TLS connection to the dnsmasq server, which obviously does not support TLS, and private domains fail to resolve.
No Public DNS Leaks
One nice side effect of this setup: your private subdomains are completely invisible. There are no public DNS records for them. They do not show up in certificate transparency logs because the wildcard cert covers them. Someone scanning your domain will find the public subdomains but have no idea the private ones exist.
The only way to discover them is to be on the VPN, at which point dnsmasq resolves them. And even if someone knows the subdomain name and tries to access it from the public internet, Caddy returns 403 based on the source IP check.
Mobile Access
WireGuard works great on phones. The official app for both iOS and Android supports split tunneling natively. I have the same config on my phone: only VPN traffic goes through the tunnel. This means I can check my git server from my phone without draining battery or slowing down everything else.
The one downside is that you have to manually toggle the VPN on when you need it. There is no automatic connection based on network or location. But honestly, it takes one tap and the connection establishes in under a second.
Resource Usage
WireGuard is absurdly lightweight. The server side runs as a single container using about 30MB of RAM. The CPU usage is essentially zero unless you are pushing serious bandwidth. On the client side, the kernel module handles everything so there is no userspace overhead.
Compare this to OpenVPN which needs a full userspace daemon, TLS handshakes, and significantly more CPU per connection. For a personal VPN with one or two clients, WireGuard is comically overpowered.
Final Thoughts
Split tunnel WireGuard gives you the best of both worlds: fast, private access to your services without compromising your normal internet experience. The setup takes maybe 30 minutes and the result is rock solid. I have had zero downtime in months.
The only complexity is the DNS piece, and even that is just a few lines in a dnsmasq config file. If you are running private services on a VPS, this is the way to expose them.