🔐
VPS

What is Headscale?

Headscale is an open-source implementation of the Tailscale control server that you can run on your own VPS instead of relying on Tailscale Inc.'s SaaS platform. Tailscale is built on WireGuard, a secure and high-performance VPN protocol, and uses a mesh network architecture that allows devices to connect directly to each other rather than through a central gateway. However, device discovery and coordination traditionally relies on Tailscale's control server in the cloud. Headscale lets you run this control server yourself, offering significant benefits: enhanced privacy (your data stays on your server), unlimited devices (no 3-device free-tier limit), full ACL control, and compliance with data residency requirements if your organization demands all data remain within your country.

In short: Headscale is an open-source Tailscale control server you deploy on your own VPS. You still use the official Tailscale client on all devices, but the coordination server stays under your control—no device limits, your data in your datacenter, and flexible ACL rules. It's privacy-first Tailscale.

Why Use Headscale Instead of Standard Tailscale

Several reasons drive organizations and privacy-conscious users toward Headscale. First, Tailscale's free tier caps you at 3 devices; Headscale has no such limit. Second, all connection metadata and IP addresses remain on your server—nothing leaves to Tailscale Inc.—crucial for privacy and compliance regimes like GDPR. Third, you gain complete control over ACL policies and can configure MagicDNS, server names, and behavior exactly as you need. Fourth, if your organization mandates that all data remain on domestic servers, Headscale enables compliance without monthly SaaS bills. Fifth, you can audit the source code (Headscale is open-source on GitHub) and customize behavior far beyond what Tailscale's closed-source control server allows.

Headscale vs. Tailscale vs. Plain WireGuard

Understanding the distinctions matters. WireGuard is a low-level VPN protocol: fast, secure, but requires manual key generation, interface configuration, and route management. Tailscale wraps WireGuard in a convenient application layer, adding automatic peer discovery, MagicDNS (resolve peers by hostname instead of IP), intelligent NAT traversal, and a web UI—but the control server is closed-source and hosted by Tailscale Inc. Headscale mimics the Tailscale control server on your own infrastructure, so you get all Tailscale's convenience (official client apps, MagicDNS, mesh routing) while the server remains under your control. Think of it as "Tailscale's features with your own infrastructure."

Warning: Headscale and Tailscale are not 100% interchangeable. For example, Headscale does not support OIDC/SSO in the same way as Tailscale for Business, and you must provision your own HTTPS certificate and domain—unlike Tailscale's frictionless signup. Evaluate both before committing.

Prerequisites and Planning

Before starting, ensure you have: (1) a VPS running Ubuntu 22.04 LTS (or similar Debian-based distribution) with root access; (2) a public IP address (not behind CGNAT—Carrier Grade NAT—since Headscale needs a stable public IP for peer discovery); (3) a DNS domain name pointing to your VPS IP (e.g., headscale.example.com) required for HTTPS certificates; (4) Docker and Docker Compose installed, or the ability to install Headscale natively; and (5) basic comfort with Linux commands, Nginx config, and firewall rules. If you don't have a DNS A record set up yet, configure it now—point your domain to your VPS's public IP before proceeding further.

Install Headscale: Docker Compose (Easiest)

Docker Compose offers the fastest setup. Verify Docker is installed:

docker --version && docker-compose --version

Create a directory for Headscale:

mkdir -p /opt/headscale/data /opt/headscale/config
cd /opt/headscale

Create docker-compose.yml:

version: '3.8'
services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    hostname: headscale
    ports:
      - "8080:8080"
      - "51820:51820/udp"
    volumes:
      - ./data:/var/lib/headscale
      - ./config.yaml:/etc/headscale/config.yaml
    environment:
      - TZ=Asia/Bangkok
    restart: unless-stopped
    networks:
      - headscale

networks:
  headscale:
    driver: bridge

Download the Headscale example config:

docker run --rm headscale/headscale:latest headscale generate config > /opt/headscale/config.yaml

Start the container:

cd /opt/headscale
docker-compose up -d
docker-compose logs -f

Verify status:

docker exec headscale headscale nodes list

Tip: If port 8080 conflicts with another service, change ports in docker-compose.yml to "8888:8080" and adjust the Nginx upstream accordingly below.

Install Headscale: Native Binary (Advanced)

For more control or to avoid Docker, install the Headscale binary directly. Download the latest release (check GitHub releases for version):

cd /tmp
HEADSCALE_VERSION=0.22.0
wget https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64 -O headscale
chmod +x headscale
sudo mv headscale /usr/local/bin/

Create system user and directories:

sudo useradd -r -s /bin/false headscale
sudo mkdir -p /etc/headscale /var/lib/headscale
sudo chown -R headscale:headscale /var/lib/headscale /etc/headscale

Create systemd service file:

sudo tee /etc/systemd/system/headscale.service > /dev/null <<'EOF'
[Unit]
Description=Headscale VPN Control Server
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=headscale
Group=headscale
WorkingDirectory=/var/lib/headscale
ExecStart=/usr/local/bin/headscale serve -config /etc/headscale/config.yaml
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable headscale

Configuring config.yaml

The generated config.yaml contains many settings. Here are the critical ones to modify. Edit the file:

server:
  listen_addr: 0.0.0.0
  listen_port: 8080
  
server_url: https://headscale.example.com

db:
  type: sqlite3
  sqlite3:
    path: /var/lib/headscale/db.sqlite3

derp:
  server:
    enabled: true
    region_id: 900
    region_code: "headscale"
    region_name: "Headscale DERP"
    stun_listen_addr: "0.0.0.0:3478"

dns:
  base_domain: example.com
  magic_dns: true
  domains:
    - example.com
    - headscale.example.com

policy:
  mode: file
  path: /etc/headscale/acl.yaml

log:
  level: info

Change example.com and headscale.example.com to your actual domain. The server_url is critical—Tailscale clients use this to locate your control server. Never use HTTP; always HTTPS. SQLite is configured by default (sufficient for most deployments). DERP (Detour Encrypted Relay Protocol) provides relay servers when direct peer-to-peer connection fails. MagicDNS allows devices to discover each other by hostname instead of IP.

Setting Up HTTPS with Nginx and Let's Encrypt

Tailscale clients enforce HTTPS on the server_url—unencrypted connections are rejected. Use Nginx as a reverse proxy in front of Headscale and Certbot to provision Let's Encrypt certificates. Install Nginx:

sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx curl

Create Nginx config for Headscale:

sudo tee /etc/nginx/sites-available/headscale > /dev/null <<'EOF'
upstream headscale_backend {
    server localhost:8080;
}

server {
    listen 80;
    listen [::]:80;
    server_name headscale.example.com;

    location / {
        proxy_pass http://headscale_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_request_buffering off;
    }
}
EOF

Enable the site:

sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx

Run Certbot to generate HTTPS certificates:

sudo certbot --nginx -d headscale.example.com

Certbot's interactive prompts will ask whether to redirect HTTP to HTTPS; choose "2" to let Certbot handle everything. It will generate a certificate and update your Nginx config automatically.

Warning: Remember to open ports 80 (HTTP) and 443 (HTTPS) on your VPS firewall for Let's Encrypt renewal and client connections. Also open UDP port 51820 for WireGuard traffic. Firewall rules vary by provider (AWS Security Groups, DigitalOcean Cloud Firewalls, etc.)—check your VPS control panel.

Setting Up Namespaces, Users, and Pre-Auth Keys

Once Headscale is running, create a namespace (think of it as a "workspace" or "team") to organize devices. For Docker:

docker exec headscale headscale namespaces create mycompany
docker exec headscale headscale users create admin

For native binary:

sudo -u headscale /usr/local/bin/headscale namespaces create mycompany
sudo -u headscale /usr/local/bin/headscale users create admin

Create a pre-auth key to allow devices to join:

docker exec headscale headscale preauthkeys create --namespace mycompany --reusable --expiration 24h

This outputs a string—save it for use on client devices. The --reusable flag allows the key to be used multiple times; --expiration sets when it expires.

Connecting Tailscale Clients

On any client device (macOS, Linux, Windows, iOS, Android), install the official Tailscale client from tailscale.com. Then, from a terminal or command prompt, run:

tailscale up --login-server=https://headscale.example.com

The --login-server flag directs the client to your Headscale server instead of the official Tailscale SaaS. The client will display a URL and ask you to paste the pre-auth key. After authentication, verify that nodes appear:

docker exec headscale headscale nodes list

Your devices should now appear with Tailscale IPs (100.x.x.x range). They can ping and SSH each other by name (if MagicDNS is enabled) or by IP address.

Managing ACLs (Access Control Lists)

The ACL file at /etc/headscale/acl.yaml (or mounted in Docker) defines which devices can communicate with which. Here's a basic example:

groups:
  "group:admin":
    - admin
  "group:developers":
    - dev1
    - dev2
    - dev3

acls:
  - action: accept
    src:
      - group:admin
    dst:
      - "*:*"
  
  - action: accept
    src:
      - group:developers
    dst:
      - "10.0.0.0/24:22"
      - "10.0.1.0/24:443"
  
  - action: deny
    src:
      - "*"
    dst:
      - "*:*"

This allows the admin group access to all devices on all ports; developers can SSH (port 22) to 10.0.0.0/24 and use HTTPS (port 443) to 10.0.1.0/24; everything else is denied. After editing the ACL file, reload Headscale:

docker exec headscale headscale serve --config /etc/headscale/config.yaml

Or for the native binary:

sudo systemctl restart headscale

Security and Maintenance

Once Headscale is operational, follow these security practices. First, firewall your VPS to allow only port 443 (HTTPS) and UDP 51820 (WireGuard) from the internet; restrict port 8080 (Headscale internal) to localhost. Second, if you generate API keys for integrations, store them in a secrets manager, not in scripts. Third, update Headscale regularly. For Docker: `docker pull headscale/headscale:latest && docker-compose up -d`. For the binary, download the latest release from GitHub. Fourth, back up the SQLite database regularly (/var/lib/headscale/db.sqlite3) via cron or your VPS backup service. Fifth, monitor logs for anomalies: for Docker, `docker logs headscale`; for the binary, `journalctl -u headscale -n 50`. Sixth, store pre-auth keys with expiration dates to limit their window of validity; rotate them periodically.

Tip: On AsiaGB's fast SSD VPS infrastructure, the SQLite database will perform smoothly even with hundreds of devices. For large deployments (thousands of devices), migrate to PostgreSQL or MySQL later—Headscale supports both via the config file without re-installing.

Summary

You now have a fully operational Headscale VPN server with privacy by default, unrestricted devices, and granular ACL control. By securing it with HTTPS, Nginx reverse proxy, and Let's Encrypt, your clients connect safely. Headscale strikes an excellent balance between WireGuard's security and Tailscale's convenience. AsiaGB's VPS infrastructure—with fast CPUs and SSD storage—is ideal for running a stable, responsive Headscale server. Connect your devices, configure ACLs to your security policy, and enjoy a private, self-managed mesh VPN network.

Frequently Asked Questions

Does Headscale require PostgreSQL or MySQL, or is SQLite sufficient?

SQLite is sufficient for small-to-medium deployments (hundreds to thousands of devices). Headscale supports SQLite, PostgreSQL, and MySQL via the config file. For very large deployments, PostgreSQL or MySQL may offer better performance, but migration is straightforward.

I lost my pre-auth key. Can I create a new one?

Yes. Generate a fresh pre-auth key with: `headscale preauthkeys create --namespace mycompany --reusable --expiration 24h`. Use this new key on the device instead.

Does Headscale support two-factor authentication (2FA)?

Headscale is simpler than official Tailscale and does not include built-in 2FA. However, you can restrict API keys and pre-auth keys by setting expiration dates. For enhanced security, use OS-level or VPS firewall rules to limit who can access the Headscale control server port.

If I shut down my Headscale control server, will clients stop working?

Tailscale devices will continue routing through cached peer routes, so existing connections remain functional. However, they cannot discover new devices, accept new ACL changes, or adjust settings. The control server orchestrates—it does not relay traffic—so downtime affects configuration, not active data transfer.

Start Your AsiaGB VPS Today

AsiaGB VPS runs on SSD storage across every plan, with Thailand or Singapore datacenter options and full root access from day one — starting at 500 THB/month (Linux), backed by a Thai support team.

See VPS Plans

View all affordable VPS Thailand plans →