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-serverflag 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
| Criterion | Tailscale Standard | Headscale on VPS |
|---|---|---|
| Monthly cost (1 seat) | $8 | VPS cost (~99 DH/month) |
| Monthly cost (5 seats) | $40 | VPS cost (~99 DH/month) |
| Monthly cost (10 seats) | $80 | VPS cost (~99 DH/month) |
| Funnel / Serve | Included | Not available |
| SSH Recording | Premium plan only | Not available |
| Maintenance | None (managed service) | ~2h/month (updates, backups) |
| Data control | Tailscale cloud | Your server |
| Node limit | 100 (Standard) | No software limit |
| User limit | No fixed limit on Standard | No 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.
Update the system and install dependencies
Connect via SSH and update packages:
apt update && apt upgrade -yInstall nginx and certbot for the HTTPS reverse proxy:
apt install -y nginx certbot python3-certbot-nginxDownload and install the Headscale package
Headscale v0.29.4 (September 2026) provides
.debpackages 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.debdpkg -i /tmp/headscale.debVerify the installation:
headscale versionshould return0.29.4. On ARM64, replacelinux_amd64withlinux_arm64.Configure Headscale
The package creates the
headscalesystem user and/etc/headscale/. Edit the minimal configuration:nano /etc/headscale/config.yamlSet 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 indns_config:magic_dns: true,base_domain: your-domain.com. Create the data directory:mkdir -p /var/lib/headscale && chown headscale:headscale /var/lib/headscale.Enable and start the service
The package installs the systemd unit automatically:
systemctl enable --now headscaleCheck status:
systemctl status headscale. Output should showActive: active (running). If there is an error, check logs:journalctl -u headscale -f. The most common error at first start is a malformedserver_url— it must start withhttps://.Set up the HTTPS reverse proxy
Obtain a Let's Encrypt certificate and configure nginx:
certbot --nginx -d headscale.your-domain.comIn 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 thathttps://headscale.your-domain.com/healthreturns{"status":"pass"}.Create a first user and an authentication pre-key
Headscale organizes nodes by users. Create your first user:
headscale users create my-teamGenerate an authentication pre-key (preauthkey) to register machines without manual approval:
headscale preauthkeys create --user my-team --expiration 24hCopy the returned key — you will need it when migrating clients. The
--reusableoption allows reusing the same key for multiple machines.Open required ports in the firewall
If your VPS uses
ufw, open the required ports:ufw allow 443/tcpufw allow 3478/udpufw allow 41641/udpIf you use
iptablesdirectly 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.
Disconnect the client from the existing Tailscale network
On each machine to migrate, disconnect the client from the Tailscale network:
tailscale logoutOn 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.
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_PREAUTHKEYOn macOS, from the terminal:
tailscale up --login-server https://headscale.your-domain.com --authkey YOUR_PREAUTHKEYIf 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 listshould show the node withonlinestatus.Verify connectivity between migrated nodes
From a migrated node, verify that other nodes are visible:
tailscale statusThe 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 ofactive (direct)confirms the WireGuard connection is established without a relay. If you enabled MagicDNS, test resolution:ping node-name.your-domain.com.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, definenameserverswith your internal DNS servers andsearch_domainsfor 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
offlineinheadscale nodes list: verify that UDP ports 3478 and 41641 are open on the VPS side. Test from an external machine withnc -vzu headscale.your-domain.com 41641. - Connection shows
relayinstead ofdirect: indirect connections via DERP occur when two nodes cannot reach each other directly (strict NAT, firewall). Runtailscale netcheckon both nodes to identify network constraints. - MagicDNS does not resolve names: verify that
magic_dns: trueandbase_domainare 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_urlinconfig.yamlmust exactly match the certificate domain. A URL starting withhttp://when nginx expectshttps://triggers an infinite redirect loop. - macOS or Windows clients do not see a
--login-serveroption 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.