← Back to DevBytes

Troubleshooting Tailscale VPN: Common Issues and Fixes

Introduction to Tailscale VPN Troubleshooting

Tailscale is a modern zero-config VPN built on top of WireGuard that creates secure peer-to-peer mesh networks between your devices. While Tailscale is designed to "just work," developers and sysadmins inevitably encounter issues related to authentication, NAT traversal, DNS resolution, routing, and firewall conflicts. This tutorial walks through the most common Tailscale problems and provides practical, tested fixes you can apply immediately.

Why Troubleshooting Tailscale Matters

When Tailscale fails, your services become unreachable, CI pipelines stall, and remote development environments go dark. Understanding the diagnostic tooling and common failure modes reduces mean time to recovery (MTTR) and helps you design more resilient network topologies. Because Tailscale operates at the intersection of local networking, NAT, DNS, and identity providers, a systematic approach is essential.

Prerequisites and Diagnostic Tooling

Before diving into specific issues, ensure you have the following:

The single most important command in your troubleshooting toolkit is tailscale status. It shows the connection state of every peer, including whether traffic is direct or relayed through DERP servers.

# View status of all peers
tailscale status

# Verbose status with more detail
tailscale status --verbose

# Check the local node's IP and identity
tailscale ip
tailscale whois <peer-ip>

For deeper inspection, use tailscale debug subcommands and the built-in netcheck utility:

# Network diagnostics including NAT type and DERP latency
tailscale netcheck

# View current DERP relay map
tailscale debug derpmap

# Capture a bug report for support
tailscale bugreport

Issue 1: Node Stuck in "Idle" or "Relay" State

Symptoms

Peers appear in tailscale status but show idle or relay "xxx" instead of direct. Latency is high, throughput is poor, and connections feel sluggish.

Root Cause

Tailscale attempts direct peer-to-peer connections using STUN and NAT traversal techniques. When both peers are behind restrictive NATs (such as symmetric NAT), a direct connection cannot be established, and traffic falls back to DERP relay servers. This is functional but slow.

Fix

Run tailscale netcheck on both peers to identify NAT types. If symmetric NAT is detected, consider enabling port forwarding on one side or deploying a custom DERP server closer to your infrastructure.

# Enable UPnP and port mapping (if supported by router)
sudo tailscale up --port=41641

# Force a specific exit node or direct connection attempt
sudo tailscale up --direct-file-startup

# Check if UDP is being blocked by inspecting netcheck output
tailscale netcheck

If UDP is blocked entirely by a corporate firewall, DERP relay is your only option. You can deploy a private DERP server to reduce latency:

# Example: running a custom DERP server in Docker
docker run -d \
  --name derper \
  --restart unless-stopped \
  -p 443:443 \
  -v /etc/derp:/certs \
  ghcr.io/tailscale/derper:latest \
  --hostname=derp.example.com \
  --certmode=manual \
  --certdir=/certs

Issue 2: Authentication and Login Failures

Symptoms

Running tailscale up produces a login URL, but after authenticating, the node does not appear in the admin console, or you receive errors like unexpected HTTP response status: 401.

Fix

First, verify the node is not already authenticated under a different tailnet. Re-authenticate explicitly:

# Log out completely
sudo tailscale logout

# Log back in with a specific auth key
sudo tailscale up --auth-key=tskey-auth-XXXXX

# Or use a reusable auth key from the admin console
sudo tailscale up --auth-key=tskey-auth-YYYYY --hostname=build-server-01

If you use an identity provider (IdP) like Okta, Google Workspace, or Microsoft Entra ID, ensure the user account has access to the tailnet. Check the admin console under Settings > DNS and Access Controls for misconfigurations.

For headless servers and CI environments, always use pre-generated auth keys with appropriate tags:

# Generate an auth key via the API
curl -X POST https://api.tailscale.com/api/v2/tailnet/-/keys \
  -H "Authorization: Bearer tskey-api-XXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "capabilities": {
      "devices": {
        "create": {
          "reusable": true,
          "ephemeral": true,
          "preauthorized": true,
          "tags": ["tag:ci"]
        }
      }
    },
    "expirySeconds": 7776000
  }'

Issue 3: DNS Resolution Problems

Symptoms

You can ping peers by IP address but not by MagicDNS hostname (e.g., build-server-01.tailnet-name.ts.net). Applications fail to resolve internal service names.

Fix

Verify MagicDNS is enabled in the admin console under DNS > Nameservers. Then check the local resolver configuration:

# Check Tailscale's local DNS state
tailscale status --json | jq .Self

# Inspect what DNS servers Tailscale is using
resolvectl status tailscale0

# On macOS
scutil --dns | grep -A 5 tailscale

# Test MagicDNS resolution directly
nslookup build-server-01.tailnet-name.ts.net
dig @100.100.100.100 build-server-01

If MagicDNS is enabled but not working, the system DNS resolver may be overriding Tailscale. On Linux with systemd-resolved, ensure the Tailscale interface is properly registered:

# Restart systemd-resolved and Tailscale
sudo systemctl restart systemd-resolved
sudo systemctl restart tailscaled

# Verify the tailscale0 interface has DNS configured
resolvectl dns tailscale0
# Expected output: 100.100.100.100

For split DNS configurations where you want internal domains resolved by a private DNS server, configure it in the admin console:

# Add a restricted nameserver for a specific domain via API
curl -X POST https://api.tailscale.com/api/v2/tailnet/-/dns/nameservers \
  -H "Authorization: Bearer tskey-api-XXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "dns": ["10.0.0.53"],
    "restrict": ["internal.corp.example.com"]
  }'

Issue 4: Subnet Routing Not Working

Symptoms

A node configured as a subnet router advertises routes, but other tailnet peers cannot reach devices in the advertised subnet.

Fix

Subnet routing requires two steps: advertising routes on the router node and accepting routes on the client nodes. A common mistake is forgetting the acceptance step.

# On the subnet router node: advertise routes
sudo tailscale up --advertise-routes=10.0.1.0/24,192.168.50.0/24

# Enable IP forwarding on the router (Linux)
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

# On client nodes: accept the advertised routes
sudo tailscale up --accept-routes

Verify the routes are approved in the admin console under the node's settings. Advertised routes must be explicitly approved by an admin before they become active.

# Verify which routes are being advertised and accepted
tailscale status --json | jq '.Self.AllowedIPs'

# Test connectivity to a device in the routed subnet
ping 10.0.1.50
traceroute 10.0.1.50

Issue 5: Exit Node Configuration Failures

Symptoms

You configure a node as an exit node, but when clients try to use it, internet connectivity is lost or traffic does not route through the exit node.

Fix

Like subnet routing, exit nodes require both advertisement and acceptance. Additionally, the exit node must have proper NAT and IP forwarding configured.

# On the exit node: advertise as exit node
sudo tailscale up --advertise-exit-node

# Enable IP forwarding (Linux)
sudo sysctl -w net.ipv4.ip_forward=1

# Configure NAT masquerading if needed
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# On client nodes: use the exit node
sudo tailscale up --exit-node=<exit-node-ip-or-hostname>

# Verify traffic is routing through the exit node
curl https://api.ipify.org
# This should return the exit node's public IP

To check whether an exit node is properly configured, use the built-in check:

# Verify exit node status
tailscale exit-node list

# Check if the current node is acting as an exit node
tailscale debug exit-node

Issue 6: Firewall and Port Conflicts

Symptoms

Tailscale connects intermittently, or specific applications fail when Tailscale is running. You may see errors about the tun interface failing to create.

Fix

Tailscale uses UDP port 41641 for direct connections and creates a tun network interface. Ensure these are not blocked by local firewalls.

# Allow Tailscale traffic through ufw (Ubuntu/Debian)
sudo ufw allow 41641/udp
sudo ufw allow in on tailscale0
sudo ufw allow out on tailscale0

# For firewalld (RHEL/CentOS/Fedora)
sudo firewall-cmd --permanent --add-port=41641/udp
sudo firewall-cmd --permanent --add-interface=tailscale0
sudo firewall-cmd --reload

# Check if another VPN is conflicting
ip link show
# Look for multiple tun/wg interfaces that might conflict

On macOS, the system may prompt for permission to load a system extension. If the prompt was dismissed, manually allow it under System Settings > Privacy & Security > Network Extensions.

Issue 7: Tailscaled Daemon Not Starting

Symptoms

The tailscale CLI returns errors like tailscaled is not running or failed to connect to local tailscaled.

Fix

# Check daemon status
sudo systemctl status tailscaled

# View daemon logs for errors
sudo journalctl -u tailscaled --no-pager -n 50

# Restart the daemon
sudo systemctl restart tailscaled

# Run tailscaled in foreground for debugging
sudo tailscaled --tun=userspace-networking --sockpath=/tmp/tailscaled.sock --verbose

If the daemon fails due to a corrupted state file, reset the local state:

# Stop the daemon
sudo systemctl stop tailscaled

# Backup and remove state
sudo mv /var/lib/tailscale/tailscaled.state /var/lib/tailscale/tailscaled.state.bak

# Start fresh
sudo systemctl start tailscaled
sudo tailscale up

On containers and minimal environments, use userspace networking mode to avoid requiring tun device access:

# Run Tailscale in userspace networking mode
tailscaled --tun=userspace-networking &
tailscale up --auth-key=tskey-auth-XXXXX

Best Practices for Reliable Tailscale Deployments

Here is an example ACL configuration that enforces least privilege:

{
  "tagOwners": {
    "tag:web": ["group:devops"],
    "tag:db": ["group:devops"],
    "tag:ci": ["autogroup:member"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["tag:web:80", "tag:web:443"]
    },
    {
      "action": "accept",
      "src": ["tag:web"],
      "dst": ["tag:db:5432"]
    },
    {
      "action": "deny",
      "src": ["*"],
      "dst": ["tag:db:*"]
    }
  ]
}

Apply this ACL via the API:

curl -X POST https://api.tailscale.com/api/v2/tailnet/-/acl \
  -H "Authorization: Bearer tskey-api-XXXXX" \
  -H "Content-Type: application/json" \
  -d @acl-policy.json

Conclusion

Tailscale simplifies secure networking, but its reliance on underlying system networking components means issues can arise from NAT types, DNS resolvers, firewalls, daemon state, and configuration mismatches between advertised and accepted routes. By mastering the diagnostic commands covered in this tutorial — tailscale status, tailscale netcheck, tailscale debug, and tailscale bugreport — you can quickly isolate and resolve the vast majority of Tailscale problems. Combine these tools with disciplined ACL policies, regular client updates, and proper subnet router and exit node configuration to maintain a fast, secure, and reliable tailnet across all your development and production environments.

— Ad —

Google AdSense will appear here after approval

← Back to all articles