Deployment guide

Migrating from Tailscale to Headscale: the break-even point

Deploy on a VPS Cloud →

Tutorial

Migrating from Tailscale to Headscale: the break-even point

Self-hosting11 min read11 steps

On April 8, 2026, Tailscale announced pricing v4: the Standard plan, formerly Starter, moves to $8 per seat per month. For a five-person team, that is $40 per month — $480 per year — for a service whose core value, the WireGuard mesh control plane, can be self-hosted on a VPS for a few euros per month. Headscale is the open source implementation of the Tailscale coordination server. Licensed under BSD-3-Clause, it speaks the same protocol as the official controller. Your existing Tailscale clients — Linux, macOS, Windows, iOS, Android — continue working without reinstallation: you only change the login URL. The network traffic itself, end-to-end encrypted with WireGuard, never passed through Tailscale anyway; only the control plane changes hands. This article gives you the exact break-even numbers, the technical prerequisites, and the three steps to migrate an existing team from Tailscale to your own control plane.

Contents· Why reconsider your mesh network in 20261/10
  1. 01Why reconsider your mesh network in 2026
  2. 02Financial analysis: the break-even point
  3. 03Tailscale Standard vs Headscale on VPS
  4. 04Technical prerequisites
  5. 05Installing Headscale on a ServOrbit VPS
  6. 06Migrating clients from Tailscale in 3 steps
  7. 07ACL and internal DNS configuration
  8. 08Hardening, backups, and automatic updates
  9. 09Troubleshooting common issues
  10. 10What Headscale does not replace

Why reconsider your mesh network in 2026

Tailscale does not carry your traffic: WireGuard packets travel directly between your machines, end-to-end encrypted. What Tailscale manages in the cloud is the control plane — public key exchange, peer discovery, 100.x.x.x address assignment, MagicDNS resolution, and DERP relay distribution. You can self-host this control plane with Headscale. In 2026, the pricing increase makes this option financially obvious for any team with at least two people on a paid plan.

  • Full data control: no list of your machines, internal IP addresses, node names or connection logs passes through a third-party server.
  • No node or user limits imposed by the software: Headscale is bounded only by your VPS resources.
  • Full compatibility with official Tailscale clients: the --login-server flag is all you need to point to your own control plane.
  • MagicDNS on your own domain: each node is reachable by its short name under the suffix you choose.
  • Optional OIDC: delegate authentication to Keycloak, Authelia, or any compatible OIDC provider.
  • Operational durability: your mesh network no longer depends on a third-party pricing decision or outage.
  • Reasonable maintenance: a package update every few weeks, a SQLite backup schedulable in one cron line.

Financial analysis: the break-even point

Tailscale Personal remains free for up to six users on non-commercial use. Once you move to the Standard plan — a professional team, multi-user access, or extended ACL features — the cost is $8 per seat per month. A 1 vCPU / 1 GB RAM VPS is sufficient to run Headscale for dozens of nodes; it runs in under 64 MB of RAM at idle. The cost of that VPS at ServOrbit starts at a few euros per month.

Tailscale Standard vs Headscale on VPS

Scroll the table

CriterionTailscale StandardHeadscale on VPS
Monthly cost (1 seat)$8VPS cost (~99 DH/month)
Monthly cost (5 seats)$40VPS cost (~99 DH/month)
Monthly cost (10 seats)$80VPS cost (~99 DH/month)
Funnel / ServeIncludedNot available
SSH RecordingPremium plan onlyNot available
MaintenanceNone (managed service)~2h/month (updates, backups)
Data controlTailscale cloudYour server
Node limit100 (Standard)No software limit
User limitNo fixed limit on StandardNo software limit

Technical prerequisites

Headscale is lightweight: an entry-level VPS handles a dozen nodes. Here are the minimum resources and ports required.

  • VPS running Debian 11/12 or Ubuntu 22.04/24.04 — 1 vCPU, 512 MB RAM minimum (Headscale runs under 64 MB idle; 1 GB recommended for headroom).
  • TCP port 443 open inbound: clients connect here for registration and configuration retrieval over HTTPS.
  • UDP port 3478 open: used for STUN negotiation (discovery of candidate addresses for direct connections).
  • UDP port 41641 open: WireGuard signaling port that Tailscale clients use to contact the coordinator.
  • A domain name or subdomain pointing to the VPS: required for a valid TLS certificate. Let's Encrypt works via certbot or the integrated nginx module.
  • Embedded SQLite: Headscale requires no external database — a single SQLite file in /var/lib/headscale/ is sufficient for hundreds of nodes.

Installing Headscale on a ServOrbit VPS

The steps below start from a fresh Debian 12 VPS. Installation takes under ten minutes.

  1. Update the system and install dependencies

    Connect via SSH and update packages:

    apt update && apt upgrade -y

    Install nginx and certbot for the HTTPS reverse proxy:

    apt install -y nginx certbot python3-certbot-nginx

  2. Download and install the Headscale package

    Headscale v0.29.4 (September 2026) provides .deb packages for amd64 and arm64. Download and install:

    curl -Lo /tmp/headscale.deb https://github.com/juanfont/headscale/releases/download/v0.29.4/headscale_0.29.4_linux_amd64.deb

    dpkg -i /tmp/headscale.deb

    Verify the installation: headscale version should return 0.29.4. On ARM64, replace linux_amd64 with linux_arm64.

  3. Configure Headscale

    The package creates the headscale system user and /etc/headscale/. Edit the minimal configuration:

    nano /etc/headscale/config.yaml

    Set at minimum: server_url: https://headscale.your-domain.com, listen_addr: 0.0.0.0:8080, db_type: sqlite3, db_path: /var/lib/headscale/db.sqlite, and in dns_config: magic_dns: true, base_domain: your-domain.com. Create the data directory: mkdir -p /var/lib/headscale && chown headscale:headscale /var/lib/headscale.

  4. Enable and start the service

    The package installs the systemd unit automatically:

    systemctl enable --now headscale

    Check status: systemctl status headscale. Output should show Active: active (running). If there is an error, check logs: journalctl -u headscale -f. The most common error at first start is a malformed server_url — it must start with https://.

  5. Set up the HTTPS reverse proxy

    Obtain a Let's Encrypt certificate and configure nginx:

    certbot --nginx -d headscale.your-domain.com

    In the generated nginx vhost, add to the location / block:

    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;

    Reload nginx: systemctl reload nginx. Verify that https://headscale.your-domain.com/health returns {"status":"pass"}.

  6. Create a first user and an authentication pre-key

    Headscale organizes nodes by users. Create your first user:

    headscale users create my-team

    Generate an authentication pre-key (preauthkey) to register machines without manual approval:

    headscale preauthkeys create --user my-team --expiration 24h

    Copy the returned key — you will need it when migrating clients. The --reusable option allows reusing the same key for multiple machines.

  7. Open required ports in the firewall

    If your VPS uses ufw, open the required ports:

    ufw allow 443/tcp
    ufw allow 3478/udp
    ufw allow 41641/udp

    If you use iptables directly or a security panel (CSF, Imunify360), add these ports to the allowed inbound port list. Verify from an external machine that the UDP ports are reachable before migrating your clients.

Migrating clients from Tailscale in 3 steps

Migration requires no reinstallation of Tailscale clients. The official client has supported the --login-server flag for several versions; you simply disconnect the client from the old network and reconnect it to your Headscale server. WireGuard traffic between nodes is not interrupted during migration — only the brief disconnect/reconnect window (a few seconds per node) causes a short interruption.

  1. Disconnect the client from the existing Tailscale network

    On each machine to migrate, disconnect the client from the Tailscale network:

    tailscale logout

    On macOS and Windows, use the system tray menu: right-click the Tailscale icon → Log out. On iOS and Android, go to app settings → Log out. This step revokes authentication on the old network but does not uninstall the client.

  2. Reconnect the client to your Headscale server

    Reconnect the client pointing to your new Headscale server. On Linux:

    tailscale up --login-server https://headscale.your-domain.com --authkey YOUR_PREAUTHKEY

    On macOS, from the terminal:

    tailscale up --login-server https://headscale.your-domain.com --authkey YOUR_PREAUTHKEY

    If you do not pass a preauthkey, the client displays an authentication URL to validate on the server side with: headscale nodes register --user my-team --key <NODE_KEY>. Verify registration: headscale nodes list should show the node with online status.

  3. Verify connectivity between migrated nodes

    From a migrated node, verify that other nodes are visible:

    tailscale status

    The list should show all nodes registered on your Headscale server with their mesh IPs (100.64.x.x). Test direct connectivity with a ping: ping 100.64.0.2. A status of active (direct) confirms the WireGuard connection is established without a relay. If you enabled MagicDNS, test resolution: ping node-name.your-domain.com.

  4. Migrate remaining nodes and close the Tailscale account

    Repeat the previous two steps for each machine. Migrate development or test machines first to validate the process, then production machines. Once all nodes are migrated and verified, you can close your Tailscale account or downgrade to the Personal plan if you still have personal use cases (six users max, free). Keep the preauthkey used or generate a new one for future nodes.

ACL and internal DNS configuration

Headscale manages access policies via a HuJSON policy file (JSON extended with comments), compatible with Tailscale ACL syntax. This file defines which users or groups can access which nodes on which ports. By default, all nodes on the same Headscale network can reach each other on all ports — this permissive behavior suits a small trusted team, but should be restricted when nodes of different trust levels (developers, clients, production servers) coexist on the same network.

  • Edit the policy file: headscale policy set --policy-file /etc/headscale/policy.hujson
  • Define user groups (groups) and per-port ACLs to segment access between dev, staging, and prod environments.
  • Enable split DNS to resolve internal names: in dns_config, define nameservers with your internal DNS servers and search_domains for search suffixes.
  • Export and version your policy file in a private Git repository — this simplifies audits and rollbacks.
  • Test policy changes on a test node before applying them to the entire network: headscale policy check.

Hardening, backups, and automatic updates

A few precautions for a robust installation. Back up the SQLite database regularly: cp /var/lib/headscale/db.sqlite /backup/headscale-$(date +%Y%m%d).sqlite — a daily cron job or a script to S3 object storage is sufficient. Also back up the private keys in /var/lib/headscale/private.key and /var/lib/headscale/noise_private.key: they sign your server's identity and cannot be regenerated without forcing all nodes to reconnect. For automatic updates, configure unattended-upgrades on Debian/Ubuntu. Restrict access to the Headscale admin API (gRPC port 50443) to 127.0.0.1 only — never expose it directly to the internet.

Troubleshooting common issues

The most common errors after migration and how to fix them.

  • Node shows offline in headscale nodes list: verify that UDP ports 3478 and 41641 are open on the VPS side. Test from an external machine with nc -vzu headscale.your-domain.com 41641.
  • Connection shows relay instead of direct: indirect connections via DERP occur when two nodes cannot reach each other directly (strict NAT, firewall). Run tailscale netcheck on both nodes to identify network constraints.
  • MagicDNS does not resolve names: verify that magic_dns: true and base_domain are defined in the Headscale config, and that the client retrieved the new DNS configuration after reconnecting (tailscale status --self).
  • Invalid TLS certificate at startup: the server_url in config.yaml must exactly match the certificate domain. A URL starting with http:// when nginx expects https:// triggers an infinite redirect loop.
  • macOS or Windows clients do not see a --login-server option in the GUI: always use the terminal for migration. The Tailscale GUI does not allow changing the coordination server; only the command line supports this.

What Headscale does not replace

Headscale implements the Tailscale control plane protocol, but not the full commercial platform feature set. Tailscale Funnel (exposing local services to the internet) and Serve (local reverse proxy) are not available in Headscale. SSH Recording (recording SSH sessions over the mesh) is a Tailscale Premium feature absent from Headscale. These gaps are documented and stable: the Headscale project actively tracks protocol compatibility, not interface feature parity. If your use case is limited to mesh connectivity, MagicDNS, and ACLs — the case for the vast majority of technical teams — Headscale covers the need entirely. If you actively use Funnel or SSH Recording, evaluate whether those features justify the cost differential before migrating.

Deploy Headscale on your VPS in a few clicks

The Headscale template in the ServOrbit marketplace pre-installs and pre-configures Headscale on a Debian VPS. Ports open, systemd service active, nginx configured: your WireGuard mesh control plane is operational in under five minutes.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab