tailscale up defaults to --accept-dns=true. The docs describe MagicDNS as a convenience feature — resolve Tailscale nodes by short name instead of IP. Every Tailscale setup guide treats it as harmless. On laptops, it is. On servers, it replaces your only DNS resolver with a userspace forwarder that isn’t ready at boot and intermittently fails on public queries.
TL;DR: MagicDNS silently rewrites /etc/resolv.conf to 100.100.100.100. On servers, this causes intermittent SERVFAIL on public domains and crash loops after reboot. Fix: disable DNS acceptance and set your own resolver.
The Symptoms #
Several Linux servers running Tailscale for mesh networking exhibited puzzling behavior:
- Outbound requests randomly timing out, 2-3 seconds before returning an error
- Refreshing works; the problem comes and goes with no pattern
- Scattered
SERVFAILandi/o timeoutentries in service logs - After a reboot, multiple services simultaneously fail to start, all reporting DNS resolution failures
If it were a network outage, all requests would fail. If it were an application bug, a reboot wouldn’t break multiple unrelated services at once. This points to something more fundamental — DNS.
Checking resolv.conf #
On the affected server:
cat /etc/resolv.conf
# resolv.conf(5) file generated by tailscale
# For more info, see https://tailscale.com/s/resolvconf-overwrite
# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN
nameserver 100.100.100.100
search your-tailnet.ts.net
If you see 100.100.100.100 with the Tailscale header — that’s your culprit. MagicDNS is enabled by default. It takes over /etc/resolv.conf, making Tailscale’s built-in forwarder the sole DNS resolver for the entire system. Every DNS query — your app resolving api.example.com, apt checking security.debian.org — goes through it.
# Verify MagicDNS is active
tailscale debug prefs 2>/dev/null | grep CorpDNS
"CorpDNS": true confirms it.
The Forwarder Is Unreliable #
MagicDNS was designed for client devices. On servers as the sole resolver, it intermittently returns SERVFAIL for perfectly valid public domains:
# Check for DNS failures in the last 24 hours (run as root)
journalctl --since "24 hours ago" | grep -i "SERVFAIL\|server misbehaving\|i/o timeout.*:53"
What showed up:
ERROR lookup app.example.com: (exchange6: SERVFAIL | exchange4: SERVFAIL) [2.17s]
ERROR lookup app.example.com: (exchange6: SERVFAIL | exchange4: SERVFAIL) [2.25s]
ERROR lookup cdn.example.com: (exchange4: SERVFAIL | exchange6: SERVFAIL) [2.16s]
Multiple public domains failing simultaneously, each taking 2+ seconds. These are ordinary domains with no relation to Tailscale — collateral damage from routing everything through MagicDNS.
The Boot Race Condition #
The worst failure mode. Timeline reconstructed from journalctl after a provider maintenance reboot:
21:58:44 System boots, tailscaled starts
21:58:44 resolv.conf overwritten → nameserver 100.100.100.100
21:58:44 Other services begin starting
21:58:55 Service A starts, attempts DNS:
ERR lookup app.example.com on 100.100.100.100:53: i/o timeout
21:59:00 ERR: server misbehaving → Service A crashes
21:59:05 systemd restarts Service A → DNS fails again → crash
21:59:11 systemd restarts again → DNS fails → crash
... (every 5 seconds for 5 minutes)
tailscaled writes resolv.conf immediately at startup, but the MagicDNS forwarder needs time to initialize — connect to the Tailscale control plane, fetch config, establish upstream DNS. During this window, 100.100.100.100 is in resolv.conf but can’t resolve anything. Every service that starts during this gap enters a crash loop.
Root Cause #
MagicDNS was designed for client devices — laptops, phones. Occasional DNS hiccups are invisible there.
On servers as the sole resolver, the design assumptions break:
- Single point of failure: all DNS depends on a userspace process. It crashes or isn’t ready → all DNS gone
- Intermittent unreliability: occasional SERVFAIL on public queries — hard to trace back to DNS
- Boot race condition: resolv.conf points to Tailscale before Tailscale is ready
- Silent takeover:
tailscale updefaults to--accept-dns=truewithout warning
With resolv.conf pointing to 1.1.1.1 or 8.8.8.8, availability is far higher. Even if Tailscale crashes entirely, DNS continues working.
Fix #
Disable MagicDNS on the server (run as root):
tailscale set --accept-dns=false
Then set your own DNS. For systems without systemd-resolved:
cat > /etc/resolv.conf << 'EOF'
nameserver 1.1.1.1
nameserver 8.8.8.8
EOF
For systems running systemd-resolved, configure it through /etc/systemd/resolved.conf instead — writing to /etc/resolv.conf directly will be overwritten.
To prevent resolv.conf from being overwritten by other processes:
chattr +i /etc/resolv.conf
chattr +i may not work on OpenVZ 7 containers — the filesystem doesn’t support it. On those systems, also check if your provider’s DHCP client is overwriting resolv.conf.
Verification #
# DNS no longer goes through Tailscale
cat /etc/resolv.conf # Should show 1.1.1.1, not 100.100.100.100
dig +short google.com # Should return instantly
# Tailscale itself is unaffected
tailscale status
tailscale ping <other-node>
--accept-dns=false persists across reboots.
What You Lose #
| Capability | Affected? |
|---|---|
| Tailscale mesh networking | Unaffected |
| SSH to this server via Tailscale | Unaffected |
tailscale status / tailscale ping | Unaffected |
| All outbound DNS from apps | Unaffected (now uses 1.1.1.1) |
| Resolving Tailscale nodes by short name on this server | Must use IP |
For servers, the last item is almost never needed. If you do need it, add specific entries to /etc/hosts.
Quick Check for Your Servers #
grep -c "100.100.100.100" /etc/resolv.conf
Returns 1? You’re on MagicDNS. Check if it’s already causing damage:
journalctl --since "24 hours ago" --no-pager | grep -c "SERVFAIL\|server misbehaving"
Non-zero means MagicDNS is already impacting your services.
Docker Users #
Containers can inherit the host’s broken DNS depending on network mode:
| Setup | Risk |
|---|---|
Bridge + explicit dns: [1.1.1.1] | Safe |
| Bridge + default DNS | May inherit host DNS |
| Host network | Fully exposed |
# Check all running containers (run as root)
docker inspect --format '{{.Name}} dns={{.HostConfig.Dns}} net={{.HostConfig.NetworkMode}}' \
$(docker ps -q)
MagicDNS is disabled on all my servers now. Two weeks, zero DNS-related incidents. The previous average was two or three per week — I just hadn’t connected them to Tailscale.