Want a private, zero-trust mesh VPN just like Tailscale – but 100% self-hosted, no cloud dependency, unlimited devices, and full privacy control?
The perfect combo is official Tailscale clients (on every platform) + Headscale – the leading open-source, self-hosted alternative to Tailscale’s coordination server.
You get the exact same magical experience: automatic NAT traversal, MagicDNS, ACLs, exit nodes, subnet routers, Tailscale SSH – but all coordination happens on your own server.
Key Benefits of Self-Hosting with Headscale
- Unlimited devices & users – no Tailscale subscription
- Complete data sovereignty & privacy (no third-party knows who connects to what)
- Identical UX on Windows, macOS, Linux, iOS, Android, iPadOS
- Great for homelabs, remote teams, small businesses, IoT fleets
- Free forever (only VPS cost ~$5–10/mo)
This 2026-updated guide uses the modern, recommended stack: Docker Compose + Caddy (automatic HTTPS).
Architecture Overview
1. Prerequisites
- VPS with Ubuntu 22.04/24.04 (DigitalOcean, Vultr) – 1–2 CPU / 2 GB RAM is plenty
- Domain or subdomain (e.g.,
headscale.addrom.com) with A record pointing to VPS public IP - Open ports: 80 (Let’s Encrypt) + 443 (HTTPS + QUIC)
- Docker & Docker Compose installed
sudo apt update && sudo apt install docker.io docker-compose-plugin -y- Basic terminal knowledge
2. Install Headscale + Caddy with Docker Compose
mkdir ~/headscale && cd ~/headscale
mkdir config libdocker-compose.yml
services:
headscale:
image: headscale/headscale:stable
container_name: headscale
volumes:
- ./config:/etc/headscale
- ./lib:/var/lib/headscale
restart: unless-stopped
command: serve
caddy:
image: caddy:latest
container_name: caddy
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped
depends_on:
- headscale
volumes:
caddy_data:
caddy_config:Caddyfile (auto HTTPS)
https://headscale.addrom.com {
reverse_proxy headscale:8080
}Download example config and customize:
cd config
curl -o config.yaml https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yamlKey changes in config.yaml
server_url: https://headscale.addrom.com
listen_addr: 0.0.0.0:8080
grpc_listen_addr: 0.0.0.0:50443
tls_letsencrypt_hostname: headscale.addrom.com
tls_letsencrypt_challenge_type: HTTP-01
dns:
magic_dns: true
base_domain: tailnet.addrom.com # different from server_url
nameservers:
global:
- 1.1.1.1
- 8.8.8.8Start everything:
cd ~/headscale
docker compose up -dVerify:
curl -I https://headscale.addrom.com/health # → 200 OK3. Create Users & Pre-Auth Keys
docker exec -it headscale sh
headscale users create myuser # or yourname, teamname, etc.
headscale preauthkeys create --user myuser --expiration 24h --reusableCopy the key (hskey-auth-...) – it’s one-time-use or reusable depending on flag.
4. Connect Devices Using Official Tailscale Clients
Linux example
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up \
--login-server=https://headscale.addrom.com \
--authkey=hskey-auth-...Windows / macOS / mobile
- Install Tailscale app from official site / stores
- In app: Use custom login server →
https://headscale.addrom.com - Or paste auth key directly
Check nodes:
docker exec headscale headscale nodes listHeadscale Web UI (Highly Recommended)
Add this service to docker-compose.yml:
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
container_name: headscale-ui
restart: unless-stoppedUpdate Caddyfile:
https://headscale.addrom.com {
reverse_proxy /web* http://headscale-ui:8080
reverse_proxy headscale:8080
}Access: https://headscale.addrom.com/web
5. Advanced Features (Just Like Official Tailscale)
- Exit Node:
tailscale up --advertise-exit-node→ approve & use via app - Subnet Router:
tailscale up --advertise-routes=192.168.1.0/24→headscale routes enable --route ... - MagicDNS: Access devices as
laptop.tailnet.addrom.com - ACLs: Edit
acl.hujsonfor fine-grained access
Troubleshooting Tips
- Connection fails? Double-check HTTPS, DNS, firewall (ufw allow 80,443)
- MagicDNS not resolving? Ensure base_domain differs from server_url
- Update:
docker compose pull && docker compose up -d
Final Thoughts
Self-hosting Tailscale-style VPN with Headscale gives you enterprise-grade mesh networking without vendor lock-in or privacy concerns. It’s rock-solid for homelabs, remote work, and small teams.
Official resources:
- Headscale: https://github.com/juanfont/headscale
- Docs: https://headscale.net
- Tailscale clients: https://tailscale.com/download
Have you set up your own Headscale instance yet? Drop your first username or any issues in the comments – happy to help!








