Playbook: Tailscale to Headscale Migration¶
What it is¶
This playbook is a step-by-step operational guide for migrating a mesh network from the Tailscale SaaS coordination server to Headscale, an open-source, self-hosted implementation of the Tailscale control server.
What problem it solves¶
It eliminates dependency on Tailscale's proprietary coordination server, providing 100% data sovereignty over your network topology. It solves the "proprietary lock-in" problem for users who require a fully self-hosted, sovereign VPN solution for their homelab.
Where it fits in the stack¶
It sits in the Operational Playbook Layer, specifically under Infrastructure Migration. It guides the transition from a managed service to a self-hosted infrastructure component.
Typical use cases¶
- Homelab Hardening: Moving your internal network control plane to hardware you own.
- Privacy Optimization: Ensuring that no metadata about your node connections ever leaves your infrastructure.
- Cost Management: Bypassing device limits on Tailscale's free tier by using your own server.
Strengths¶
- Sovereignty: Complete control over your coordination server.
- Cost: No per-device or per-user fees (limited only by your hardware).
- Integration: Seamlessly integrates with Authentik for OIDC-based identity management.
Limitations¶
- Operational Burden: You are responsible for the availability and security of the Headscale server.
- Complexity: Requires managing OIDC, SSL certificates, and server backups.
When to use it¶
- When you have a stable, self-hosted identity provider like Authentik.
- When your homelab has grown beyond the scope of Tailscale's free tier or privacy policies.
When not to use it¶
- If you require the "it just works" simplicity of the Tailscale SaaS.
- If you don't have the technical expertise to manage a critical piece of networking infrastructure.
Getting started¶
Deployment¶
To begin the migration: 1. Deploy Headscale: Follow the Headscale Service guide to set up the server. 2. Back up Tailscale: Document your existing node names and ACLs. 3. Perform a Pilot: Migrate a single non-critical node first using the steps in this playbook.
Agent-Assisted Migration¶
Modern agents can significantly simplify the migration process. Use an early January 2027-class agent (e.g., Claude 5.6, GPT-5.6, Gemini 4.0 Ultra, Llama 4, or Qwen 3.8) integrated with Model Context Protocol (MCP 3.1 / FastMCP 3.1) to:
- Translate ACLs: Convert Tailscale policy.hujson to Headscale-compatible YAML/ACL formats.
- Automate Client Rollout: Script the tailscale logout and tailscale up --login-server commands across a fleet of Linux nodes via SSH using MCP-enabled terminal tools.
- Validate OIDC Config: Verify the config.yaml parameters against your Authentik provider metadata.
Migration Workflow¶
flowchart TD
Start[Current: Tailscale SaaS] --> Backup[Back up node names & ACLs]
Backup --> Deploy[Deploy Headscale Server]
Deploy --> Auth[Integrate Authentik OIDC]
Auth --> NodeMigrate{Migrate Node}
NodeMigrate --> Logout[tailscale logout]
Logout --> Login[tailscale up --login-server URL]
Login --> OIDC[OIDC Authentication]
OIDC --> Approve[Headscale Node Approval]
Approve --> Verify[Verify Connectivity]
Verify --> End[Target: Self-hosted Mesh]
Migration Steps¶
Prerequisites¶
- A functional Authentik instance for OIDC.
- A public FQDN with valid SSL certificates (e.g., via Let's Encrypt) pointing to your Headscale server.
- Tailscale clients installed on target nodes.
Step 1: Headscale Deployment¶
- Deploy Headscale using Docker (see Headscale Service for compose snippet).
- Configure
config.yamlwith yourserver_url. - Integrate with Authentik for OIDC to allow family members to join easily.
Step 2: Client Migration (Manual)¶
On each node currently running Tailscale, perform the following:
Linux¶
You can use the provided migration script:
./scripts/headscale_migration.sh https://<headscale-fqdn>
MacOS / Windows¶
- Hold the Alt (or Option) key and click the Tailscale icon in the menu bar/system tray.
- Select Change Server....
- Enter your Headscale FQDN:
https://<headscale-fqdn>. - Follow the OIDC login flow.
Step 3: Headscale Node Approval¶
If not using OIDC or if a node requires manual registration:
1. Run tailscale up --login-server https://<headscale-fqdn> on the client.
2. Copy the provided URL.
3. On the Headscale server:
headscale nodes register --user <username> --key <node-key>
Step 4: Node-Specific Checklists¶
TrueNAS SCALE NAS¶
- [x] SSH into TrueNAS.
- [x] Run
tailscale logout. - [x] Run
tailscale up --login-server https://<headscale-fqdn>. - [x] Verify NAS is reachable via Tailscale IP in Headscale.
K3s Compute Node¶
- [x] Ensure
tailscaleis running on the host. - [x] Run migration script or manual commands.
- [x] Update any K3s service advertisements if using Tailscale IPs for cluster communication.
Home Assistant VM¶
- [x] Use the HA Terminal & SSH add-on.
- [x] Execute
tailscale logoutfollowed bytailscale up --login-server .... - [x] Re-verify HA external access if proxied through Tailscale.
Step 5: Verification¶
- List nodes on Headscale:
headscale nodes list. - Verify connectivity between nodes:
tailscale ping <other-node-ip>. - Ensure ACLs are correctly migrated if using a custom
policy.hujson.
Rollback Plan¶
If migration fails, logout from Headscale and login back to Tailscale:
tailscale logout
tailscale up
Troubleshooting Migration Issues¶
- OIDC Redirect Loops: Often caused by mismatched
server_urlin Headscale andredirect_urisin Authentik. Use Claude 5.6, GPT-5.6, or Gemini 4.0 Ultra to inspect the logs:docker logs headscale. - Node Name Conflicts: Headscale requires unique node names per user. If a migration fails due to naming, use
headscale nodes rename. - Pre-Auth Key Expiry: If migrating headless nodes, ensure the pre-auth keys generated on the server have sufficient TTL.
CLI examples¶
Client Migration (Linux)¶
# Logout from Tailscale SaaS
tailscale logout
# Connect to a Headscale instance
tailscale up --login-server https://headscale.example.com
Headscale Server Management¶
# Register a node manually
headscale nodes register --user jules --key nodekey:abcdef123456
# List nodes and their status
headscale nodes list
# Create a pre-authenticated key for headless nodes
headscale preauthkeys create -u jules --expiration 24h
API examples¶
Querying Node Status via REST API¶
Agents can use the Headscale REST API to verify migration status across the fleet.
# Fetch all nodes from Headscale
curl -X GET \
-H "Authorization: Bearer $HEADSCALE_API_KEY" \
https://headscale.example.com/api/v1/node
Scripted Node Approval¶
import requests
from pydantic import BaseModel, Field, ValidationError
class NodeRegistration(BaseModel):
user: str = Field(..., min_length=1, description="The username to register the node under")
key: str = Field(..., pattern=r"^(nodekey|mkey):[a-f0-9]+$", description="The node key generated by the client")
def approve_node(headscale_url: str, api_key: str, user: str, node_key: str) -> bool:
try:
# Strict Pydantic v2 validation
reg = NodeRegistration(user=user, key=node_key)
except ValidationError as e:
print(f"Validation error: {e}")
return False
endpoint = f"{headscale_url.rstrip('/')}/api/v1/node/register"
headers = {"Authorization": f"Bearer {api_key}"}
payload = reg.model_dump()
response = requests.post(endpoint, headers=headers, json=payload)
return response.status_code == 200
# Example usage
# approve_node("https://headscale.local", "secret_key", "jules", "nodekey:abcdef123456")
Related tools / concepts¶
- Headscale Service
- Authentik Service
- Invisible Kubernetes
- K3s Cluster Setup
- Infrastructure Architecture
- SSO Comparison
- Family Admin Automation
- Tailscale Service
Sources / References¶
Contribution Metadata¶
- Last reviewed: 2027-01-07
- Confidence: high