Tailscale is one of the best mesh VPN experiences available. Install the client, sign in, and your devices find each other across any network. The problem: Tailscale’s control plane is proprietary, hosted on their infrastructure, and you can’t self-host it. Your network metadata, device inventory, and connection logs live on someone else’s servers.
Headscale fixes that. It’s an open-source reimplementation of Tailscale’s coordination server that works with the official Tailscale clients. You get the same client experience — the same apps on every platform — but your control plane runs on your own infrastructure.
This guide walks through deploying Headscale with Docker Compose, connecting Tailscale clients, and configuring OIDC authentication, DERP relay, and access policies.
What You Get (and What You Don’t)
What works with Headscale:
- Official Tailscale clients on Windows, macOS, Linux, iOS, Android
- MagicDNS (automatic hostname resolution between peers)
- ACLs (access control policies)
- Exit nodes and subnet routers
- OIDC authentication (Google, Azure AD, Okta, Keycloak)
- DERP relay servers (embedded or external)
- Pre-auth keys for automated device registration
- 24K+ GitHub stars, active community
What may not work (or lags behind):
- Tailscale Funnel (exposing services to the internet)
- Tailnet Lock (device approval)
- Some newer Tailscale features that require server-side changes
- Official Tailscale support (you’re on your own, plus the community)
Prerequisites
- A VPS or server with a public IP (1 vCPU, 512 MB RAM minimum)
- Docker and Docker Compose installed
- A domain name pointing to your server (for HTTPS)
- A reverse proxy (Caddy, Nginx, or Traefik) for TLS termination
For Docker fundamentals, see our Docker deep dive.
Step 1: Create Directory Structure
mkdir -p ~/headscale/{config,data}
cd ~/headscale
Step 2: Download the Configuration File
curl -o config/config.yaml \
https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml
Step 3: Edit the Configuration
Open config/config.yaml and update these key settings:
# The URL your clients will connect to
server_url: https://hs.yourdomain.com
# Listen address
listen_addr: 0.0.0.0:8080
# Database (SQLite is fine for most deployments)
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
# IP prefix for your network
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
# DNS configuration
dns:
magic_dns: true
base_domain: tail.yourdomain.com
nameservers:
global:
- 1.1.1.1
- 8.8.8.8
Step 4: Create the Docker Compose File
# docker-compose.yml
services:
headscale:
image: headscale/headscale:latest-alpine
container_name: headscale
restart: unless-stopped
command: serve
volumes:
-./config:/etc/headscale
-./data:/var/lib/headscale
ports:
- "8080:8080"
- "9090:9090" # metrics (optional)
cap_add:
- NET_ADMIN
- SYS_MODULE
Step 5: Start Headscale
docker compose up -d
docker compose logs -f headscale
You should see Headscale starting and listening on port 8080.
Step 6: Create a User
Headscale organizes devices under users (similar to Tailscale’s tailnets):
docker compose exec headscale headscale users create myuser
Step 7: Generate a Pre-Auth Key
docker compose exec headscale headscale preauthkeys create \
--user myuser \
--reusable \
--expiration 24h
Save the key that’s returned. You’ll need it to register devices.
Step 8: Connect Tailscale Clients
On each device you want to connect, install the official Tailscale client, then point it at your Headscale server:
Linux:
tailscale up --login-server https://hs.yourdomain.com --authkey YOUR_PREAUTH_KEY
macOS/Windows:
Use the Tailscale GUI and set the login server URL in the preferences, or use the CLI:
tailscale login --login-server https://hs.yourdomain.com --authkey YOUR_PREAUTH_KEY
iOS/Android:
The official Tailscale apps support custom login servers. In the app settings, set the control server URL to your Headscale instance.
Step 9: Verify Connectivity
Check that your devices see each other:
tailscale status
You should see all registered devices with their Headscale-assigned IPs. Test connectivity:
tailscale ping <device-name>
Configuring OIDC Authentication
For teams, you’ll want SSO instead of pre-auth keys. Headscale supports OIDC with any provider.
Add to your config.yaml:
oidc:
issuer: https://accounts.google.com # or your OIDC provider
client_id: your-client-id
client_secret: your-client-secret
scope:
- openid
- profile
- email
allowed_domains:
- yourdomain.com
With OIDC enabled, users authenticate through your identity provider when they first connect their Tailscale client. No more manually distributing pre-auth keys.
This pairs well with an open-source identity provider like Zitadel if you want the full stack self-hosted.
Setting Up a DERP Relay Server
DERP (Designated Encrypted Relay for Packets) servers relay traffic when direct peer-to-peer connections fail (restrictive NAT, firewalls). Headscale includes an embedded DERP server.
Enable it in config.yaml:
derp:
server:
enabled: true
region_id: 999
region_code: "custom"
region_name: "My DERP"
stun_listen_addr: 0.0.0.0:3478
urls: [] # disable external DERP maps
auto_update_enabled: false
Make sure UDP port 3478 (STUN) is open on your server. The DERP relay ensures your peers can always connect, even behind restrictive firewalls.
Access Control Policies
Headscale uses ACL policies identical to Tailscale’s format. Create an ACL policy file:
docker compose exec headscale headscale policy set --file /etc/headscale/acl.json
Example acl.json:
{
"acls": [
{
"action": "accept",
"src": ["group:developers"],
"dst": ["tag:servers:"]
},
{
"action": "accept",
"src": ["group:admins"],
"dst": [":*"]
}
],
"groups": {
"group:developers": ["user1@yourdomain.com"],
"group:admins": ["admin@yourdomain.com"]
},
"tagOwners": {
"tag:servers": ["group:admins"]
}
}
This gives developers access to tagged servers, while admins can reach everything.
Adding a Web UI
Headscale doesn’t ship with a web interface, but community projects fill the gap:
- Headscale-UI: A simple web interface for managing users, nodes, and pre-auth keys
- Headplane: A more feature-rich management UI with policy editing
Deploy Headscale-UI alongside Headscale:
# Add to your docker-compose.yml
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
container_name: headscale-ui
restart: unless-stopped
ports:
- "8443:443"
Headscale vs NetBird: Which to Choose?
Both are open-source, self-hosted mesh VPN solutions. The choice depends on your starting point:
| Factor | Headscale | NetBird |
|---|---|---|
| Clients | Official Tailscale clients | NetBird agents |
| Migration from Tailscale | Seamless (same clients) | Requires new agents |
| Built-in web UI | No (community options) | Yes (Control Center) |
| Zero-trust model | ACL-based (Tailscale-style) | Identity-based policies |
| API for automation | Limited | Full REST API + CLI |
| License | BSD-3 | BSD-3 |
Choose Headscale if you’re already running Tailscale clients and want to migrate to self-hosting without touching every endpoint.
Choose NetBird if you’re starting fresh and want a more complete platform with built-in web management, API automation, and identity-based zero-trust policies. Read our NetBird review for the full picture.
Troubleshooting
Client Can’t Reach the Login Server
Verify your reverse proxy is correctly forwarding to port 8080, and that TLS is properly configured. Headscale requires HTTPS for client connections.
Devices Registered but Can’t Communicate
Check that both devices are registered under the same user, and that your ACL policies allow the traffic. If you have no ACL policy set, Headscale defaults to allowing all traffic between devices of the same user.
MagicDNS Not Resolving
Ensure magic_dns: true is set in your config and that the base_domain is configured. After changing DNS settings, restart Headscale and reconnect clients:
docker compose restart headscale
tailscale down && tailscale up --login-server https://hs.yourdomain.com
Conclusion
Headscale gives you Tailscale’s client experience with full control over your coordination server. For existing Tailscale users, it’s the lowest-friction path to self-hosting. For new deployments, consider whether Headscale’s Tailscale-compatible approach or NetBird’s all-in-one platform better fits your needs.
For the broader VPN landscape, see our complete guide to self-hosted VPN solutions in 2026.