Install Headscale VPN on VPS Instead of Tailscale
Headscale is an open-source implementation of the Tailscale control server that lets you run a mesh VPN privately using any Tailscale client app (iOS, Android, Mac, Windows, Linux), but with your control plane hosted on your own VPS instead of Tailscale's cloud infrastructure. This means all routing data and network information stays under your complete control.
Install Headscale on Your VPS
# Download headscale binary
HEADSCALE_VERSION=$(curl -s https://api.github.com/repos/juanfont/headscale/releases/latest | grep tag_name | cut -d'"' -f4)
wget "https://github.com/juanfont/headscale/releases/download/${HEADSCALE_VERSION}/headscale_linux_amd64"
sudo mv headscale_linux_amd64 /usr/local/bin/headscale
sudo chmod +x /usr/local/bin/headscale
# Or install using Docker:
mkdir -p ~/headscale/config ~/headscale/data
Create Configuration File
sudo mkdir -p /etc/headscale
sudo nano /etc/headscale/config.yaml
server_url: https://hs.yourdomain.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
- 100.64.0.0/10
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
tls_letsencrypt_hostname: hs.yourdomain.com
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
log:
level: info
Set Up systemd Service
sudo useradd -m -d /var/lib/headscale -s /usr/sbin/nologin headscale
sudo mkdir -p /var/lib/headscale
sudo chown headscale:headscale /var/lib/headscale
sudo nano /etc/systemd/system/headscale.service
[Unit]
Description=headscale controller
After=network.target
[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve
Restart=unless-stopped
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now headscale
Create Users and Preauthentication Keys
# Create a namespace/user
sudo headscale users create myteam
# Generate preauthentication key for new devices
sudo headscale preauthkeys create --user myteam --reusable --expiration 24h
# View created keys
sudo headscale preauthkeys list --user myteam
Add Devices Using Tailscale Client
# On Linux device:
curl -fsSL https://tailscale.com/install.sh | sh
# Connect to Headscale instead of Tailscale cloud
sudo tailscale up --login-server https://hs.yourdomain.com --authkey PREAUTH_KEY_HERE
# View nodes in Headscale
sudo headscale nodes list
--advertise-exit-node on the VPS you want to act as exit node, then approve it in the headscale CLI.
Your VPS running Headscale should have low latency to most devices in your network. For teams in Thailand and Southeast Asia, Singapore VPS from asiagb.com provides low latency across the region and is ideal for hosting your Headscale control server. Before deployment, verify your DNS and port configuration using dnsxray.com to ensure your domain points correctly and port 443 is open.
Verify Headscale is Working Correctly
# Check service status
sudo systemctl status headscale
# List connected nodes
sudo headscale nodes list
# View network routes
sudo headscale routes list
# Test connectivity between nodes
# From one node, ping another using Tailscale IP
ping 100.64.0.2
tailscale status on all devices to verify all peers are connected correctly, and use tailscale ping PEER_IP to test connectivity between devices.
Common Issues & Troubleshooting
Device Cannot Connect to Headscale
Verify DNS resolution for hs.yourdomain.com and ensure port 443 is open. Check Headscale logs for errors:
sudo journalctl -u headscale -f
# Look for TLS or connection refused errors
SSL Certificate Fails to Issue (LetsEncrypt Error)
Port 80 must be open for ACME challenges, or use DNS challenge instead:
sudo ufw allow 80
sudo systemctl restart headscale
Nodes Cannot Ping Each Other Even After Connecting
This may be due to DERP server configuration. Try enabling the built-in DERP server:
# In /etc/headscale/config.yaml
derp:
server:
enabled: true
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
Preauthentication Key Expired
Preauthentication keys have a limited lifetime and must be regenerated periodically:
sudo headscale preauthkeys create --user myteam --reusable --expiration 7d
Frequently Asked Questions
How does Headscale differ from raw WireGuard?
WireGuard is a low-level VPN protocol that requires manual configuration of every peer. Headscale/Tailscale uses WireGuard as the underlying transport layer but automates key exchange and routing through a control plane. Adding new devices is significantly simpler and doesn't require manual configuration of each connection.
Does Headscale support iOS and Android?
Yes. Use the official Tailscale app on iOS and Android, then go to Settings, change the coordination server to your Headscale URL, and log in with your preauthentication key.
Is Headscale free? Are there device limits?
Headscale is 100% open-source with no cost and no device limits, unlike Tailscale's Free tier which limits devices to 100 and restricts certain features behind a paywall.