Headscale Setup Guide: Self-Host Your Own Tailscale Control Server

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.