starmorph logo
Published on

Tailscale 101: Complete Developer Reference Guide for Mesh VPN Networking

Tailscale 101: Complete Developer Reference Guide for Mesh VPN Networking

Tailscale is a WireGuard-based mesh VPN that makes secure networking dead simple. Instead of configuring firewall rules, managing VPN servers, or distributing SSH keys, Tailscale creates encrypted peer-to-peer connections between your devices with zero configuration. Whether you're connecting to a homelab from a coffee shop, linking cloud VMs across providers, or giving your CI/CD runners access to internal services — Tailscale handles the networking so you can focus on building.

This guide is a complete reference covering everything from Tailscale's architecture and full CLI reference to advanced features like ACLs, Tailscale SSH, Serve & Funnel, Docker integration, and troubleshooting. All information current as of February 2026.

Check out Starmorph Kit — starter kits and templates for building your next project faster.

Table of Contents

What is Tailscale?

Tailscale is a WireGuard-based mesh VPN built to favor direct peer-to-peer (P2P) connections whenever possible. The architecture separates the control plane from the data plane:

  • Control Plane: Hub-and-spoke design that carries virtually no traffic, only exchanging tiny encryption keys and policies. The coordination server is part of the control plane, not the data plane.
  • Data Plane: Nearly all data is sent P2P over end-to-end encrypted WireGuard tunnels, not through any central server.

Coordination Server (Control Plane)

The coordination server manages authentication, node authorization, peer discovery, ACL enforcement, network map distribution, and encryption key exchange. It works as an exchange point of WireGuard public keys for nodes in the Tailscale network.

Communication Protocol: Modern clients use the Noise protocol (Noise_IK) for authenticated encryption. The control server advertises its own curve25519 public key, and client/server communicate via ECDH using small NaCl crypto_box messages.

Resilience: If the coordination server goes down, every Tailscale node caches its state in memory — all existing connections keep working.

Data Plane: WireGuard, DERP, NAT Traversal

Tailscale's base layer is the userspace Go variant of WireGuard (wireguard-go). Device private encryption keys never leave their respective nodes — the coordination server only collects and exchanges public keys.

DERP (Designated Encrypted Relay for Packets) is a TCP-based relay operating over TLS on port 443 that forwards encrypted packets when peers can't reach each other directly. Every connection technically begins via DERP, then upgrades to a direct link if possible. Internal metrics show >90% success rates for direct NAT traversal.

NAT Traversal combines STUN discovery, the Disco protocol for peer-to-peer discovery, racing strategy to find the fastest path, and adaptive heartbeats to maintain connections.

Peer Relays (October 2025)

Peer Relays let you designate your own nodes as dedicated traffic relays within your tailnet. Benefits include full control over capacity, no QoS throttling, and performance rivaling direct connections. Real-world improvements show ~150ms latency reduction and 12.5x throughput increase.

Routing preference order:

  1. Direct connection (NAT traversal succeeds)
  2. Peer relay connection (dedicated capacity)
  3. DERP relayed connection (shared infrastructure)

Complete CLI Reference

The Tailscale CLI uses the format: tailscale [flags] <subcommand> [command flags]

The CLI is a standalone binary acting as a LocalAPI client — all operations are performed by sending HTTP requests to the tailscaled daemon's LocalAPI endpoint.

Core Commands

tailscale up

Connect to Tailscale and join your tailnet.

tailscale up [flags]

Authentication & Identity:

  • --authkey=KEY — Use a pre-generated authentication key
  • --hostname=NAME — Set device hostname

Networking:

  • --accept-routes — Accept subnet routes advertised by other nodes
  • --advertise-routes=CIDR1,CIDR2 — Advertise specific IP routes
  • --advertise-exit-node — Advertise this node as an exit node
  • --exit-node=IP|HOSTNAME — Route all non-Tailscale traffic through specified exit node
  • --exit-node-allow-lan-access — Allow LAN access when using exit node
  • --accept-dns — Accept DNS configurations from tailnet

Security:

  • --shields-up — Block all incoming connections over Tailscale
  • --ssh — Enable Tailscale SSH on this device

Tags:

  • --advertise-tags=tag:server,tag:prod — Apply ACL tags

tailscale down

Disconnect from Tailscale without logging out.

tailscale down

tailscale set

Configure options persistently (survives restarts). Modern alternative to passing options to tailscale up — only updates explicitly specified fields.

tailscale set --accept-routes
tailscale set --advertise-exit-node
tailscale set --hostname myserver
tailscale set --ssh

tailscale status

Show state of tailscaled and its connections.

tailscale status          # Table format
tailscale status --json   # JSON output
tailscale status --active # Active connections only

Network Diagnostics

tailscale ip              # Show Tailscale IPs (both IPv4/IPv6)
tailscale ip -4           # IPv4 only
tailscale ip -6           # IPv6 only
tailscale ping <host>     # Ping at the Tailscale layer
tailscale netcheck        # Analyze local network conditions
tailscale whois <IP>      # Show machine/user for a Tailscale IP

tailscale ssh

SSH to a Tailscale machine using WireGuard authentication (no SSH keys needed).

tailscale ssh <hostname>
tailscale ssh user@hostname

tailscale nc

Netcat-like TCP connection through Tailscale.

tailscale nc <hostname> <port>
ssh -o ProxyCommand='tailscale nc %h %p' user@hostname.tailnet.ts.net

File Transfer & Sharing

tailscale file cp <file> <hostname>:       # Send files via Taildrop
sudo tailscale file get                     # Receive files (Linux)
sudo tailscale file get --loop              # Continuous receive mode
tailscale drive share <name> <path>         # Share directory (Taildrive)

Service Exposure

tailscale serve 8080                        # Serve on tailnet only
tailscale serve ~/public                    # Serve a directory
tailscale funnel 8080                       # Expose to public internet
tailscale cert myserver.tailnet.ts.net      # Get TLS certificate

Account & Maintenance

tailscale login                             # Log in
tailscale logout                            # Log out and expire node key
tailscale switch                            # Switch between accounts
sudo tailscale update                       # Update to latest version
tailscale version                           # Print version
tailscale bugreport                         # Generate shareable bug report ID

Installation

macOS

Three options:

  1. Download from Tailscale (recommended): Download the standalone variant from tailscale.com/download
  2. Mac App Store: Search for "Tailscale"
  3. CLI-only: Download from GitHub releases

Ubuntu / Debian

# Quick install (all versions)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Manual install:

curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | sudo apt-key add -
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt-get update
sudo apt-get install tailscale
sudo tailscale up

Supports Ubuntu 16.04 through 25.04 and Debian 9 through 12.

Raspberry Pi

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Supports Raspberry Pi OS (32-bit and 64-bit), Stretch, Buster, and Bullseye.

Docker

Official image: tailscale/tailscale (Docker Hub and GitHub Packages). See the Docker Integration section for usage patterns.

Post-Installation

After installing, connect to your tailnet:

sudo tailscale up

Follow the authentication URL to log in with your identity provider.

MagicDNS

MagicDNS automatically assigns DNS names to all devices in your tailnet — no need to track IP addresses.

  • Enabled by default
  • Local DNS resolution at 100.100.100.100
  • Routes queries to appropriate upstream resolvers by domain suffix

Naming Convention

Tailnet name format: tail<hex>.ts.net

Nodes get automatic DNS names based on their hostnames with numeric suffixes for conflicts:

myserver.tail12abc.ts.net
laptop.tail12abc.ts.net
pi.tail12abc.ts.net

Custom Domains

You can use custom domains (e.g., app.example.com) for services, though this currently requires third-party DNS solutions or manual DNS configuration. Native naming uses the *.ts.net structure.

ACLs (Access Control Lists)

Tailscale ACLs are JSON-formatted rules controlling who can access what within your network. Default deny — access must be explicitly granted.

Core Components

  1. Groups — Collections of users
  2. Tags — Labels assigned to devices
  3. ACLs — Rules defining access
  4. SSH — SSH-specific rules
  5. Auto-approvers — Pre-grant permissions

Example ACL Policy

{
  "groups": {
    "group:engineering": ["user1@example.com", "user2@example.com"],
    "group:ops": ["admin@example.com"]
  },
  "tagOwners": {
    "tag:server": ["group:ops"],
    "tag:database": ["group:ops"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:server:*"]
    },
    {
      "action": "accept",
      "src": ["group:ops"],
      "dst": ["tag:database:5432"]
    }
  ],
  "ssh": [
    {
      "action": "accept",
      "src": ["group:ops"],
      "dst": ["tag:server"],
      "users": ["ubuntu", "root"]
    }
  ],
  "autoApprovers": {
    "routes": {
      "10.0.0.0/8": ["ops@example.com"],
      "192.168.0.0/24": ["tag:router"]
    },
    "exitNode": ["group:ops"]
  }
}

Best Practices

  1. Use groups rather than individual users for easier onboarding/offboarding
  2. Tag servers by roletag:production, tag:staging, tag:database
  3. Be specific — only grant the access needed
  4. Default deny — nothing is allowed unless explicitly granted

Tailscale SSH

Tailscale SSH establishes SSH connections between devices without managing SSH keys. It authenticates using WireGuard instead of passwords or SSH keys.

Setup

1. Enable on the target machine:

tailscale set --ssh

2. Configure ACL rules (see ACLs section for SSH rule examples).

3. Connect:

tailscale ssh hostname
tailscale ssh user@hostname
# Or use traditional SSH:
ssh user@hostname.tailnet.ts.net

Key Features

  • SSO and MFA enforcement tied to your identity provider
  • Session recording for compliance and forensics
  • Centralized ACLs for access management
  • No key management — no distributing, rotating, or revoking SSH keys

Tailscale Serve & Funnel

Tailscale Serve

Makes a local service accessible to devices in your tailnet only.

Prerequisites: Enable HTTPS Certificates in admin console, then run tailscale cert.

tailscale serve 8080                          # Serve localhost:8080 on tailnet
tailscale serve https:443 / localhost:8080    # HTTPS on port 443
tailscale serve ~/public                      # Serve a directory

Tailscale Funnel

Makes a local service accessible to the entire internet.

When you turn on Funnel, Tailscale creates public DNS records for your node.tailnet.ts.net name, pointing to worldwide ingress servers.

tailscale funnel 8080                         # Expose to public internet

Use cases: Testing OAuth callbacks, receiving webhooks, sharing dev environments temporarily.

Benefits: Public IP, DNS, TLS cert, and HTTPS server — all automatic.

Subnet Routers & Exit Nodes

Subnet Routers

Access resources on a remote network through your Tailscale connection.

Setup:

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

# 2. Advertise routes
tailscale up --advertise-routes=192.168.1.0/24,10.0.0.0/8

# 3. Approve routes in admin console

# 4. Accept routes on client devices
tailscale set --accept-routes

Exit Nodes

Route all non-Tailscale internet traffic through a specific device.

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

# On the client:
tailscale set --exit-node=<IP|hostname>
tailscale up --exit-node=<IP|hostname> --exit-node-allow-lan-access

Use cases: Encrypting traffic on public Wi-Fi, browsing from a different location, securing traffic through a trusted device.

Taildrop (File Sharing)

Transfer files between devices via encrypted P2P connections. Only sending and receiving devices have access.

CLI Usage

# Send
tailscale file cp myfile.txt myserver:
tailscale file cp document.pdf 100.64.0.5:

# Receive (Linux — must manually run)
sudo tailscale file get
sudo tailscale file get --loop    # Continuous mode

macOS/Windows: Files automatically appear in Downloads folder. Mobile: Use the share menu and select Tailscale.

Interrupted transfers can resume where they left off for up to one hour.

Tailscale on Proxmox

Tailscale can run on the Proxmox host, VMs (straightforward), or LXC containers (requires special config).

LXC Container Setup

Tailscale needs /dev/net/tun, which is not allowed in LXC by default:

  1. Go to container Resources tab
  2. Select AddDevice Passthrough
  3. Enter dev/net/tun in the Device Path field

Then install normally:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

VM Setup

Standard installation — no special configuration needed.

Auth Keys

Auth keys allow devices to join your tailnet automatically without interactive authentication.

TypeDescriptionUse Case
ReusableCan authenticate multiple serversBatch server deployments
EphemeralDevices auto-removed when offlineContainers, serverless, CI/CD
Pre-authorizedDevices automatically approvedAutomated deployments
TaggedDevice identity is the tag, not a userService accounts, IaC

Using Auth Keys

tailscale up --authkey=tskey-auth-XXXXX

Create auth keys in Settings → Keys in the admin console, or via API with an OAuth client.

Tailscale API

Base URL: https://api.tailscale.com/api/v2

OAuth Scopes

  • devices:core — Read/write devices, authorize, manage tags
  • dns:read — Read DNS settings
  • acls:read / acls:write — Access control policies
  • routes:read / routes:write — Subnet routes

Tokens expire after one hour.

Key Endpoints

# Get access token
curl -X POST https://api.tailscale.com/api/v2/oauth/token \
  -u "client_id:client_secret" \
  -d "grant_type=client_credentials"

# List devices
curl -H "Authorization: Bearer tstoken-XXXXX" \
  https://api.tailscale.com/api/v2/tailnet/example.com/devices

Terraform Integration

Tailscale has an official Terraform provider:

resource "tailscale_acl" "example" {
  acl = jsonencode({
    acls = [
      {
        action = "accept"
        src    = ["group:engineering"]
        dst    = ["tag:server:*"]
      }
    ]
  })
}

Docker Integration

Tailscale runs in Docker using the sidecar pattern.

Docker Compose Example

version: '3.8'
services:
  tailscale:
    image: tailscale/tailscale:latest
    hostname: myapp
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
    volumes:
      - tailscale-state:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    restart: unless-stopped

  myapp:
    image: myapp:latest
    network_mode: service:tailscale
    depends_on:
      - tailscale

volumes:
  tailscale-state:

The network_mode: service:tailscale setting routes all traffic for myapp through the Tailscale container. Each container becomes a fully-fledged node on your tailnet with its own DNS name.

Common Patterns

Homelab Networking

  • Install Tailscale on your firewall (OPNsense, pfSense) as a subnet router
  • Use sidecar containers to connect Docker services directly
  • Combine with cloud instances for hybrid setups

Site-to-Site Networking

Connect two subnets using subnet routers on each side. Devices in the subnets don't need Tailscale installed.

CI/CD Integration

# GitHub Actions
- name: Connect to Tailscale
  uses: tailscale/github-action@v4
  with:
    authkey: ${{ secrets.TAILSCALE_AUTHKEY }}

Tag runners by repository, team, or job type and define granular ACL policies.

Pricing Tiers

PlanPriceDevicesBest For
Free$0Up to 100Personal use
Starter$6/user/mo100 + (users x 10)Small teams
Premium$18/user/mo100 + (users x 10)Growing teams
EnterpriseContactCustomOrganizations

Free plan for qualifying open source GitHub organizations is also available.

Troubleshooting

Network Diagnostics

tailscale netcheck                    # Check UDP, DERP latency, NAT traversal
tailscale status --json               # Detailed connection state
tailscale ping <hostname>             # Test Tailscale-layer connectivity

DNS Issues

  • dns-forward-failing health warning: Check upstream resolver configuration
  • After WiFi changes: Tailscale may fail to set DNS resolvers — restart tailscaled
  • Docker environments: In-container resolver needs upstream configured or returns SERVFAIL

Logs & Debugging

# Linux
sudo journalctl -u tailscaled

# Bug reports
tailscale bugreport

# Advanced debugging
tailscale debug localapi    # Direct HTTP calls to LocalAPI

Firewall

In most cases, no firewall rules needed. If allowlisting is required, log.tailscale.com resolves to 199.165.136.0/24 (IPv4) and 2606:B740:1::/48 (IPv6).

Additional Security: Tailnet Lock

Tailnet lock verifies public keys distributed by the coordination server before trusting them. Nodes must be signed by a trusted tailnet lock key. This mitigates the risk of the coordination server being compromised.

tailscale lock [subcommand]

Shields Up

Block all incoming Tailscale connections while keeping outgoing connections functional:

tailscale set --shields-up

Tailscale eliminates the complexity of traditional VPN setups while providing enterprise-grade security through WireGuard encryption. Whether you're running a homelab, connecting cloud infrastructure, or securing CI/CD pipelines, Tailscale's zero-config mesh networking gets you connected in minutes. The free tier supports up to 100 devices — more than enough for most developers and small teams to build a complete private network.