A zero-configuration mesh VPN built on WireGuard that connects devices across networks with automatic NAT traversal and centralized access control.

Addresses below are RFC 5737 documentation ranges or placeholders - swap in your own.

Table of Contents#

  1. Overview
  2. Key Features
  3. Architecture
  4. Installation
  5. Basic Usage
  6. Subnet Routing
  7. Exit Nodes
  8. MagicDNS
  9. Tailscale SSH
  10. Tailscale Funnel
  11. Access Control Lists (ACLs)
  12. Tailscale Lock (Tailnet Lock)
  13. Admin Console
  14. Headscale (Self-Hosted)
  15. Troubleshooting
  16. See Also
  17. Sources

1. Overview#

Tailscale is a mesh VPN that creates secure, peer-to-peer connections between devices using the WireGuard protocol. Instead of routing traffic through a central server, Tailscale establishes direct encrypted tunnels between nodes wherever possible. A coordination server handles authentication, key distribution, and ACL enforcement, but never sees user traffic.

Tailscale is available as a managed SaaS product (free tier supports up to 100 devices) and as an open-source coordination server alternative called Headscale.

2. Key Features#

  • Zero configuration - no manual port forwarding, firewall rules, or key management needed
  • WireGuard-based - uses the WireGuard protocol for fast, modern encryption
  • Automatic NAT traversal - uses DERP relay servers and NAT hole-punching to connect devices behind firewalls
  • MagicDNS - automatic DNS for all devices in the network using stable hostnames
  • ACL-based access control - fine-grained, centrally managed policies define who can access what
  • SSO integration - authenticates via existing identity providers (Google, Microsoft, GitHub, Okta, OIDC)
  • Subnet routing - expose entire LAN subnets to the tailnet without installing Tailscale on every device
  • Exit nodes - route all internet traffic through a specific node
  • Tailscale SSH - SSH access managed through Tailscale ACLs, replacing SSH keys
  • Funnel - expose local services to the public internet through Tailscale's infrastructure

3. Architecture#

3.1. Components#

  • Tailscale client (tailscaled) - daemon running on each device that manages WireGuard tunnels
  • Coordination server - distributes public keys and ACL policies; never handles user traffic
  • DERP relays - encrypted relay servers used when direct peer-to-peer connections fail; traffic remains end-to-end encrypted
  • Identity provider - handles authentication (Google, Microsoft, GitHub, Okta, or custom OIDC)

3.2. Connection Flow#

1. Device authenticates via identity provider
2. Coordination server distributes peer keys and ACL policies
3. Devices attempt direct WireGuard connection (NAT hole-punching)
4. If direct connection fails, traffic relays through DERP servers
5. DERP relay sees only encrypted WireGuard packets

3.3. Network Model#

Each device receives a stable IP address in the 100.64.0.0/10 (CGNAT) range. IPv6 addresses are assigned from the fd7a:115c:a1e0::/48 prefix. These addresses persist across network changes and device reboots.

4. Installation#

4.1. Arch Linux#

sudo pacman -S tailscale
sudo systemctl enable --now tailscaled

4.2. Debian/Ubuntu#

curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list

sudo apt update
sudo apt install tailscale
sudo systemctl enable --now tailscaled

Replace noble with your Ubuntu release codename. For Debian, use the Debian-specific URL from pkgs.tailscale.com.

4.3. Fedora/RHEL#

sudo dnf config-manager --add-repo https://pkgs.tailscale.com/stable/fedora/tailscale.repo
sudo dnf install tailscale
sudo systemctl enable --now tailscaled

4.4. Docker#

docker run -d \
  --name=tailscale \
  --hostname=<container-hostname> \
  --cap-add=NET_ADMIN \
  --cap-add=NET_RAW \
  -v /dev/net/tun:/dev/net/tun \
  -v tailscale-state:/var/lib/tailscale \
  -e TS_AUTHKEY=<auth-key> \
  tailscale/tailscale:latest

5. Basic Usage#

5.1. Authenticate and Connect#

# Start Tailscale and authenticate
sudo tailscale up

# Use an auth key for headless/automated setups
sudo tailscale up --authkey=<auth-key>

# Disconnect
sudo tailscale down

# Check status
tailscale status

# Show detailed peer information
tailscale status --peers --active

5.2. Useful Commands#

# Ping a peer (via Tailscale, shows direct vs relayed)
tailscale ping <peer-name-or-ip>

# Show current Tailscale IP addresses
tailscale ip

# Show network diagnostics
tailscale netcheck

# Show debug information
tailscale debug metrics
tailscale debug prefs

5.3. File Transfer#

# Send a file to another device
tailscale file cp <file> <peer-name>:

# Receive files (waits for incoming transfers)
tailscale file get <output-directory>

6. Subnet Routing#

Subnet routers expose local network subnets to the tailnet, allowing devices without Tailscale installed to be accessed through the VPN.

6.1. Enable Subnet Router#

On the machine that will act as the subnet router:

# Enable IP forwarding
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl --system

# Advertise the subnet
sudo tailscale up --advertise-routes=192.0.2.0/24,198.51.100.0/24

6.2. Approve the Route#

Routes must be approved in the admin console:

  1. Go to login.tailscale.com/admin/machines
  2. Click the subnet router machine
  3. Under "Subnets", approve the advertised routes

Or approve via CLI with tailscale set:

# On the admin's machine (if using ACL auto-approvers)
# Add to ACL file under "autoApprovers":
{
  "autoApprovers": {
    "routes": {
      "192.0.2.0/24": ["tag:server"]
    }
  }
}

6.3. Accept Routes on Clients#

Clients must explicitly accept subnet routes:

sudo tailscale up --accept-routes

7. Exit Nodes#

An exit node routes all internet traffic from other tailnet devices through itself, similar to a traditional VPN gateway.

7.1. Advertise as Exit Node#

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

Approve the exit node in the admin console (same process as subnet routes).

7.2. Use an Exit Node#

# Route all traffic through a specific exit node
sudo tailscale up --exit-node=<exit-node-ip-or-name>

# Allow LAN access while using an exit node
sudo tailscale up --exit-node=<exit-node-ip-or-name> --exit-node-allow-lan-access

# Stop using the exit node
sudo tailscale up --exit-node=

8. MagicDNS#

MagicDNS provides automatic DNS resolution for all devices in a tailnet. Each device is reachable by its hostname.

8.1. Enable MagicDNS#

Enable in the admin console under DNS settings, or configure in the ACL file:

{
  "dns": {
    "magic": true,
    "nameservers": ["1.1.1.1", "8.8.8.8"],
    "routes": {
      "corp.example.com": ["192.0.2.53"]
    },
    "domains": ["example.com"]
  }
}

8.2. DNS Resolution#

# Devices are reachable by hostname
ping my-laptop

# Full domain format
ping my-laptop.<tailnet-name>.ts.net

# Check DNS configuration
tailscale dns status

8.3. Split DNS#

Route specific domains to internal DNS servers while using Tailscale DNS for everything else:

{
  "dns": {
    "routes": {
      "internal.corp.com": ["192.0.2.53"],
      "staging.corp.com": ["198.51.100.53"]
    }
  }
}

9. Tailscale SSH#

Tailscale SSH allows SSH connections managed entirely through Tailscale ACLs, eliminating the need for SSH key distribution or password management.

9.1. Enable Tailscale SSH#

# On the target machine
sudo tailscale up --ssh

9.2. Configure ACLs for SSH#

{
  "ssh": [
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["autogroup:self"],
      "users": ["autogroup:nonroot", "root"]
    },
    {
      "action": "check",
      "src": ["autogroup:member"],
      "dst": ["tag:production"],
      "users": ["root"],
      "checkPeriod": "12h"
    }
  ]
}

Actions:

  • accept - allow SSH access immediately
  • check - require re-authentication via the identity provider at the specified interval

9.3. Connect via Tailscale SSH#

# SSH using Tailscale identity (no keys needed)
ssh <user>@<tailscale-hostname>

# Tailscale handles authentication and authorization
# No SSH keys are exchanged; identity comes from the IdP

10. Tailscale Funnel#

Funnel exposes local services to the public internet through Tailscale's infrastructure, with automatic HTTPS certificates.

10.1. Enable Funnel#

# Serve a local service publicly
tailscale funnel 8080

# Serve with a specific path
tailscale funnel --set-path=/api localhost:3000

# Serve a local directory
tailscale funnel /path/to/files

# Disable funnel
tailscale funnel --remove 8080

10.2. Tailscale Serve (Private, Tailnet-Only)#

tailscale serve is similar to Funnel but only exposes services within the tailnet:

# Expose a local port within the tailnet
tailscale serve 8080

# Expose with HTTPS (auto-certs within tailnet)
tailscale serve https / localhost:8080

# Show current serve configuration
tailscale serve status

10.3. Funnel ACL Requirement#

Funnel must be enabled in the ACL policy:

{
  "nodeAttrs": [
    {
      "target": ["autogroup:member"],
      "attr": ["funnel"]
    }
  ]
}

11. Access Control Lists (ACLs)#

ACLs are defined as a JSON (or HuJSON) policy file in the admin console. They control all traffic within the tailnet.

11.1. Basic Structure#

{
  "acls": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:webserver:80,443"]
    },
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["autogroup:self:*"]
    }
  ],

  "groups": {
    "group:engineering": ["user1@example.com", "user2@example.com"],
    "group:devops": ["user3@example.com"]
  },

  "tagOwners": {
    "tag:webserver": ["group:devops"],
    "tag:database": ["group:devops"]
  },

  "hosts": {
    "monitoring": "100.64.0.5"
  }
}

11.2. Common ACL Patterns#

Allow all members to access all devices (default):

{
  "acls": [
    {"action": "accept", "src": ["*"], "dst": ["*:*"]}
  ]
}

Role-based access:

{
  "acls": [
    {"action": "accept", "src": ["group:devops"], "dst": ["*:*"]},
    {"action": "accept", "src": ["group:engineering"], "dst": ["tag:webserver:80,443", "tag:webserver:22"]},
    {"action": "accept", "src": ["group:engineering"], "dst": ["tag:database:5432"]}
  ]
}

Deny by default with explicit allows:

{
  "acls": [
    {"action": "accept", "src": ["group:admins"], "dst": ["*:*"]},
    {"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self:*"]},
    {"action": "accept", "src": ["tag:monitoring"], "dst": ["*:9090,9100"]}
  ]
}

11.3. Tests#

ACL policies support inline tests to verify correctness:

{
  "tests": [
    {
      "src": "user1@example.com",
      "accept": ["tag:webserver:80"],
      "deny": ["tag:database:5432"]
    }
  ]
}

12. Tailscale Lock (Tailnet Lock)#

Tailnet Lock adds a second layer of verification: even if the coordination server is compromised, devices can only join the network if signed by a trusted node.

12.1. Enable Tailnet Lock#

# Initialize tailnet lock (generates a signing key)
tailscale lock init

# Sign a new node
tailscale lock sign <node-key>

# View lock status
tailscale lock status

# List trusted signing keys
tailscale lock list

12.2. Key Concepts#

  • Tailnet Lock Key (TLK) - a signing key held by trusted nodes
  • Nodes must be signed by a TLK holder before they can join the network
  • Protects against coordination server compromise
  • Requires at least one TLK holder to be online to sign new devices

12.3. Disable Tailnet Lock#

# Requires signatures from a majority of TLK holders
tailscale lock disable

13. Admin Console#

The admin console at login.tailscale.com provides:

  • Machines - view all devices, approve subnet routes and exit nodes, manage tags
  • Users - manage users and their devices
  • ACLs - edit access control policies with syntax validation and testing
  • DNS - configure MagicDNS, nameservers, and split DNS routes
  • Keys - generate auth keys, API keys, and manage OAuth clients
  • Logs - network flow logs showing source, destination, and action (accept/deny)
  • Settings - identity provider configuration, tailnet lock, feature toggles

13.1. Auth Keys#

Generate pre-authentication keys for headless or automated device registration:

# In the admin console: Settings > Keys > Generate auth key
# Options:
#   - Reusable: allow multiple devices to use the same key
#   - Ephemeral: device is removed when it goes offline
#   - Pre-approved: skip manual approval
#   - Tags: automatically apply tags on registration

13.2. API Access#

# Generate an API key or OAuth client in the admin console
# Use with the Tailscale API
curl -s -H "Authorization: Bearer <api-key>" \
  https://api.tailscale.com/api/v2/tailnet/-/devices | jq .

14. Headscale (Self-Hosted)#

Headscale is an open-source, self-hosted implementation of the Tailscale coordination server.

# Install headscale (example for Debian)
wget https://github.com/juanfont/headscale/releases/latest/download/headscale_<version>_linux_amd64.deb
sudo dpkg -i headscale_<version>_linux_amd64.deb

# Configure /etc/headscale/config.yaml
# Start the server
sudo systemctl enable --now headscale

# Create a user
headscale users create myuser

# Generate a pre-auth key
headscale preauthkeys create --user myuser

# Connect a client to headscale
sudo tailscale up --login-server=https://headscale.example.com

Key differences from managed Tailscale:

  • No MagicDNS (requires manual DNS configuration)
  • No Funnel support
  • No Tailscale SSH (use standard SSH)
  • ACLs configured in a YAML file on the server
  • DERP servers must be self-hosted or use Tailscale's public ones

Troubleshooting#

IssueCauseSolution
"Unable to connect" after tailscale uptailscaled service not runningRun sudo systemctl start tailscaled then sudo tailscale up
All traffic relayed (no direct connections)Firewall blocking UDP hole-punchingRun tailscale netcheck to diagnose; ensure UDP port 41641 is not blocked
Subnet routes not workingRoutes not approved or client not accepting routesApprove routes in admin console; run sudo tailscale up --accept-routes on clients
DNS resolution fails for tailnet namesMagicDNS not enabled or systemd-resolved conflictEnable MagicDNS in admin console; check resolvectl status for DNS routing
Exit node not appearingExit node not approved in admin consoleApprove the exit node in the Machines tab of the admin console
"not logged in, last login error"Auth key expired or revokedGenerate a new auth key and re-authenticate with sudo tailscale up
High latency between peersTraffic routing through DERP relay instead of directRun tailscale ping <peer> to check; firewalls or strict NAT may prevent direct connections
Tailscale SSH "connection refused"SSH not enabled on the target or ACL denying accessVerify --ssh flag is set on the target; check SSH ACL rules in the policy
Cannot reach devices after OS updatetailscaled service not started after rebootRun sudo systemctl enable --now tailscaled

See Also#

Sources#