
September 12, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
You need secure remote access to staging servers, office LANs, and client infrastructure without exposing SSH to the public internet. Tailscale solves that with a zero-config mesh VPN, but the hosted control plane adds per-user cost and stores your network map on someone else's servers. When you Self-Host Tailscale with Headscale, you keep the same Tailscale client while running the coordination server yourself—ideal for agencies, small DevOps teams, and anyone who already manages Linux servers in Nepal or abroad. This guide walks through a production-ready Headscale install on Ubuntu 24.04 with Docker, ACLs, DNS, and the operational habits that keep the mesh stable.
--login-server, register nodes via CLI or OIDC, and manage ACLs in a local policy file—same WireGuard mesh, your infrastructure.What Is Headscale and Why Self-Host Tailscale with Headscale?
Headscale is an open-source, self-hosted implementation of the Tailscale coordination server. It does not replace the Tailscale client on your laptops and servers. Instead, it replaces Tailscale's cloud control plane—the component that distributes node keys, ACL policies, and DNS settings across your mesh.
The official Tailscale client still handles WireGuard tunnels, NAT traversal, and peer discovery. Headscale only coordinates who is allowed to talk to whom. That split matters when you read the docs: client behaviour comes from Tailscale's installation guides; server behaviour comes from the Headscale project on GitHub.
On real client projects I maintain several sister sites on shared EC2 infrastructure with Deployer 7 and GitLab CI. SSH bastions work, but a mesh VPN simplifies ad-hoc access to staging boxes and internal services. For a deeper comparison of mesh VPN versus jump hosts, see our guide on SSH bastion host patterns and the companion piece on Tailscale zero-config mesh VPN.
When Headscale Makes Sense
- You have five to five hundred nodes and want predictable cost (your VPS bill, not per-seat SaaS pricing).
- Compliance or client policy requires the network map and ACL definitions to stay on infrastructure you control.
- You already run Ubuntu servers and want one more lightweight service alongside existing stacks.
- You need custom OIDC integration with your identity provider without Tailscale's enterprise tier.
When to Stay on Tailscale SaaS
Hosted Tailscale includes MagicDNS, subnet routing UI, and support out of the box. If your team is two people and billing is not a concern, SaaS is simpler. Headscale adds operational responsibility: backups, TLS renewal, upgrades, and debugging coordination failures yourself.
| Criteria | Tailscale SaaS | Headscale Self-Hosted |
|---|---|---|
| Control plane location | Tailscale cloud | Your VPS or on-prem server |
| Cost model | Per-user/month (free tier capped) | Server cost only (~Rs 800–2,500/mo, ~USD 6–18) |
| ACL management | Web admin + API | HuJSON policy file + CLI |
| Operational burden | Low | Medium—backups, TLS, upgrades |
| Client compatibility | Official Tailscale client | Same official client |
| Subnet routers / exit nodes | Full support | Supported with manual config |
What Server Specs and Prerequisites Do You Need for Headscale?
Headscale is lightweight. A 1 vCPU, 1 GB RAM Ubuntu 24.04 VPS handles dozens of nodes comfortably. SQLite is fine for small deployments; switch to PostgreSQL when you expect hundreds of nodes or want easier backup tooling.
Before you install anything, prepare these items:
- A public hostname, e.g.
headscale.example.com, with an A record pointing at your server. - TLS certificate—Let's Encrypt via Caddy or Certbot on Nginx.
- UDP port 41641 open for direct WireGuard (Headscale advertises this; nodes may also use DERP relays).
- TCP 443 (or 8080 behind a reverse proxy) for the Headscale API and client registration.
- Docker Engine 24+ or a direct binary install—Docker is easier to upgrade.
If you are provisioning the host itself, our domain registration and hosting service covers DNS and VPS setup for Nepal-based teams who prefer local billing in NPR.
Minimum Software Versions
Run Headscale on Ubuntu 22.04 or 24.04 with Docker 24+. The Headscale container image tracks releases on GitHub; pin a specific tag in production rather than using latest. Client-side, install the current Tailscale package from Tailscale's official repo—the client version should be within the compatibility range noted in the Headscale release notes.
How Do You Install and Configure Headscale on Ubuntu?
The fastest path is Docker Compose with a bind-mounted config directory. Create a dedicated user, a config folder, and a persistent data volume for SQLite or PostgreSQL connection strings.
Step 1: Create the Directory Layout
sudo mkdir -p /opt/headscale/{config,data}
sudo chown -R $USER:$USER /opt/headscale
cd /opt/headscale Step 2: Write config.yaml
The config file defines your server URL, listen address, database, and DNS settings. Adjust the server URL to match your public hostname—clients embed this value during login.
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
ip_prefixes:
- 100.64.0.0/10
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default.json
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
dns:
override_local_dns: true
nameservers:
global:
- 1.1.1.1
- 8.8.8.8
log:
level: info Step 3: Docker Compose File
services:
headscale:
image: headscale/headscale:0.23
container_name: headscale
restart: unless-stopped
command: headscale serve
volumes:
- ./config:/etc/headscale
- ./data:/var/lib/headscale
ports:
- "127.0.0.1:8080:8080" Do not expose port 8080 publicly without TLS. Put Caddy or Nginx in front:
headscale.example.com {
reverse_proxy localhost:8080
} Start the stack with docker compose up -d. Verify with curl -I https://headscale.example.com/health—you should get HTTP 200.
How Do You Register Tailscale Clients with Your Headscale Server?
Each logical group of machines in Headscale is called a user (formerly namespace). Create one per team or environment—production, staging, developers.
Create a User and Pre-Auth Key
docker exec headscale headscale users create production
docker exec headscale headscale preauthkeys create --user production --reusable --expiration 24h Copy the generated key. On each machine, install Tailscale, then point the client at your server:
sudo tailscale up \
--login-server=https://headscale.example.com \
--authkey=tskey-auth-xxxxxxxx \
--accept-routes \
--accept-dns=false On Linux servers you typically want --accept-dns=false so Headscale does not override /etc/resolv.conf on production boxes. Workstations can accept DNS if you configure MagicDNS equivalents in Headscale.
Approve Nodes (If Not Using Pre-Auth Keys)
Interactive registration generates a URL the user opens in a browser. Headscale prints a pending node; an admin approves it:
docker exec headscale headscale nodes list
docker exec headscale headscale nodes register --user production --key NODEKEY Confirm connectivity with tailscale ping target-hostname or by pinging the assigned 100.x address from another node.
Subnet Routers and Exit Nodes
To reach an entire LAN—common when you self-host services like those described in our MinIO self-hosted S3 storage guide—advertise routes from one node:
sudo tailscale up \
--login-server=https://headscale.example.com \
--advertise-routes=192.168.1.0/24 \
--authkey=tskey-auth-xxxxxxxx Enable the route in Headscale:
docker exec headscale headscale routes list
docker exec headscale headscale routes enable --route-id 1 For projects like Adventure Third Pole Trek, where booking staff need VPN access to a staging Laravel app, subnet routing beats opening MySQL or Redis ports to the world.
How Do You Write Headscale ACL Policies for Production?
ACLs live in a HuJSON file—JSON with comments—mounted into the Headscale config directory. Start restrictive: deny by default, allow only what each group needs.
{
"groups": {
"group:admins": ["admin@example.com"],
"group:devs": ["dev1@example.com", "dev2@example.com"]
},
"acls": [
{
"action": "accept",
"src": ["group:admins"],
"dst": ["*:*"]
},
{
"action": "accept",
"src": ["group:devs"],
"dst": ["tag:staging:*"]
}
],
"tagOwners": {
"tag:staging": ["group:admins"]
}
} Reload policy after edits:
docker exec headscale headscale policy check --file /etc/headscale/acl.hujson
docker exec headscale headscale policy set --file /etc/headscale/acl.hujson Tag nodes during registration or afterward:
docker exec headscale headscale nodes tag --identifier 3 --tags tag:staging Store ACL files in Git. Treat them like firewall rules—review changes, test in staging, then apply. A JSON formatter helps validate syntax before you push policy to production.
What Production Operations Keep Headscale Reliable?
Running the control plane is the easy part. Keeping it reliable for eighteen months is where teams stumble. These habits come from maintaining production Linux hosts alongside application stacks.
Back Up the Database
SQLite lives in your data volume. Schedule nightly copies:
0 2 * * * cp /opt/headscale/data/db.sqlite /backups/headscale-$(date +\%F).sqlite For PostgreSQL, use pg_dump and test restores quarterly. Losing the database does not kill existing WireGuard tunnels immediately, but new nodes cannot register and ACL updates stop propagating.
Monitor TLS Expiry and Service Health
Caddy renews automatically; Certbot needs a systemd timer. Add an HTTP check against /health from an external monitor. If Headscale is down, existing peers keep talking, but policy changes stall.
Upgrade Strategy
Read Headscale release notes before upgrading—breaking changes to config keys happen. Pin image tags, bump one minor version at a time, and keep a snapshot of the data directory. Roll back by reverting the Compose tag and restarting.
Logging and Debugging
When a node shows Logged out or cannot register, check three places: Headscale logs (docker logs headscale), client logs (journalctl -u tailscaled), and whether the server's TLS certificate matches server_url exactly. A hostname mismatch produces cryptic client errors that look like network failures.
Pair Headscale with other self-hosted tooling only when it simplifies ops. Our n8n self-hosted workflow automation and self-hosted CI runners guides follow the same pattern: one VPS, Docker Compose, strict firewall, off-site backups.
Security Hardening Checklist
- Restrict Headscale admin CLI access to operators with SSH keys—no shared root passwords.
- Use short-lived pre-auth keys for automated server provisioning; rotate after CI runs.
- Enable UFW: allow 22 from your office IP, 443 public, 41641/udp public.
- Generate strong keys with a password generator for any OIDC client secrets.
- Audit registered nodes monthly; remove laptops belonging to former staff immediately.
If you prefer not to operate the control plane yourself, support and maintenance services can cover Headscale upgrades, backup verification, and ACL reviews alongside your application stack.
Key Takeaways
- Headscale replaces Tailscale's cloud control plane—you still use the official Tailscale client for WireGuard tunnels.
- Pin Docker image tags, terminate TLS at Caddy or Nginx, and never expose Headscale HTTP directly to the internet.
- Create per-environment users, register nodes with pre-auth keys, and verify with
tailscale ping. - Store ACL HuJSON in Git, default deny, and tag servers by role (
tag:staging,tag:production). - Back up the SQLite or PostgreSQL database nightly—a control-plane loss blocks new registrations and policy updates.
- Subnet routers let you reach whole LANs without opening public ports on internal services.
People Also Ask
Does Headscale work with the official Tailscale client?
Yes. Install Tailscale from the vendor repository on each device. Pass --login-server=https://your-headscale-domain during tailscale up. Do not use forked clients—the official build expects a coordination server speaking the Tailscale protocol, which Headscale implements.
Can Headscale replace a VPN appliance for remote workers?
For most small and mid-size teams, yes. Workers install Tailscale, join your Headscale user group, and reach office subnets through an advertised route. You still need a machine on the LAN acting as subnet router. Traditional IPsec appliances offer more legacy protocol support; Headscale wins on setup speed and per-device management.
Is Headscale free to use in production?
Headscale is open source under the BSD licence. Your costs are the VPS, domain, and operator time—not per-seat licensing. Budget roughly Rs 1,000–2,500 per month (~USD 8–20) for a control-plane server in Nepal or Singapore region hosting, plus the time to patch and back it up.
What happens if the Headscale server goes offline?
Existing WireGuard peer connections keep working until keys expire or nodes restart. New devices cannot register, ACL changes do not propagate, and nodes that reboot may fail to re-authenticate. Run Headscale on a monitored VPS with automated restarts and tested backups to limit outage impact.
Run Your Own Mesh VPN Control Plane
You now have a complete path to Self-Host Tailscale with Headscale: provision a small Ubuntu box, deploy Headscale behind TLS, register clients with pre-auth keys, and enforce least-privilege ACLs from Git. The mesh gives your team SSH, database, and admin-panel access without punching holes in public firewalls—the same pattern I use alongside Deployer releases on shared EC2 hosts and Laravel on AWS EC2 stacks. If you want the VPN layer designed, hardened, and maintained alongside your web apps, contact us or explore custom software development and web development services. Read more self-hosting guides on the blog, browse Notary Kathmandu and other portfolio projects running on production Linux infrastructure, or learn about the author on the about me page.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

