I had AdGuard set up as my local DNS resolver and everything looked fine right up until I tried to open a Tailscale URL to one of my VM instances.
Ordinary LAN names resolved just fine. The *.ts.net name did not. DNS can look so simple but oftentimes it is not.
What was happening
The machine was sending normal DNS queries to AdGuard, but it was not accepting Tailscale’s DNS settings. That meant MagicDNS had nowhere to hook in on the client side.
So the Tailscale connection itself was healthy, but the hostname was dead from the NixOS machine.
What I first blamed
I checked the obvious stuff first:
- AdGuard was still answering local DNS.
- Tailscale connectivity was up.
- The target host was reachable once I had an IP.
So this was really a resolver problem. A little annoying.
The actual issue
My NixOS config had Tailscale DNS disabled:
extraSetFlags = [
"--accept-dns=false"
];At the same time, systemd-resolved was not active, and NetworkManager was forcing DNS to AdGuard. That meant there was no split-DNS path for Tailscale names at all.
The fix
I kept AdGuard as the default DNS resolver, but enabled Tailscale DNS acceptance and turned on systemd-resolved so split DNS could actually work:
services.tailscale = {
enable = true;
extraSetFlags = [
"--accept-dns=true"
];
};
networking.networkmanager = {
enable = true;
dns = "systemd-resolved";
};
services.resolved.enable = true;The wired profile still kept AdGuard as the normal resolver:
ignore-auto-dns = true;
dns = "192.168.10.20;";The above IP is a sample IP.
NOTE
That was the point. I did not want to give up local filtering just to make one Tailscale hostname work.
Quick checks
After the change, I verified three things:
- Nix evaluation showed
services.resolved.enable = true - NetworkManager was using
systemd-resolved - Tailscale had
--accept-dns=true
Then resolvectl could resolve the Tailscale hostname to the expected tailnet address.
resolvectl query yourtailscaleurl.ts.netI also hit the URL over HTTPS to make sure the path actually worked end to end.
One small gotcha: curl -I returned HTTP/2 501, but that was just because that specific service does not implement HEAD. DNS, Tailscale, TLS, and the app were all fine. The HTTP method was the wrong test.
What I learned
If I want AdGuard and Tailscale MagicDNS to coexist, I need client-side split DNS. Just forcing everything at one resolver definitely makes an issue with the Tailscale hostnames.
Also, an HTTP status code from curl -I is not always a network problem. Sometimes it is just the server saying, that HEAD is not its thing.