
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
You want the simplicity of Tailscale’s WireGuard mesh networking but cannot accept a third-party coordination server holding your network topology and user metadata. The solution is to self-host Tailscale with Headscale, an open-source implementation of the Tailscale control protocol that runs entirely on your own infrastructure. This approach gives you full data sovereignty, removes vendor lock-in, and allows operation in air-gapped or compliance-restricted environments where external SaaS dependencies are prohibited.
How does Headscale enable you to self-host Tailscale with Headscale?
Tailscale’s magic lies not in WireGuard itself—which is just encrypted UDP tunnels—but in the coordination server that handles key exchange, NAT traversal (DERP), and access policy distribution. When you self-host Tailscale with Headscale, you replace only this coordination layer. The actual data plane remains identical: standard Tailscale clients establish direct peer-to-peer WireGuard connections whenever possible, falling back to your self-hosted DERP relay only when direct paths fail.
Understanding this separation is critical. Many engineers assume self-hosting means replacing the entire stack. In practice, you keep the battle-tested Tailscale client binaries on every endpoint. You gain sovereignty over metadata and policy without reimplementing complex NAT traversal logic. For teams managing hardened Ubuntu servers or compliance-bound infrastructure, this distinction matters: your audit scope covers the coordination server and DERP relay, not the WireGuard kernel module on every laptop.
What infrastructure do you need before deploying Headscale?
A common mistake is under-provisioning the coordination server. Headscale itself is lightweight, but its database backend determines reliability. For production use when you self-host Tailscale with Headscale, plan for these components:
- Headscale server: 2 vCPU, 4 GB RAM minimum. Runs as a single binary or container. Exposes HTTP (8080) for client registration and gRPC/HTTPS (9090) for admin CLI.
- PostgreSQL: Do not use SQLite in production. Concurrent node check-ins during outages cause write contention. A managed RDS instance or a properly tuned PostgreSQL deployment with streaming replication is strongly recommended.
- DERP relay: At least one geographically close to your primary user base. If your team is in Nepal and India, a Mumbai or Singapore VPS provides better fallback latency than Frankfurt. Run at least two DERP nodes for redundancy.
- TLS termination: Headscale requires HTTPS. Use Nginx or Caddy as a reverse proxy with valid certificates from Let’s Encrypt. Never expose raw HTTP to Tailscale clients.
- DNS: A dedicated subdomain like
headscale.yourdomain.comandderp.yourdomain.com. Avoid sharing with application domains to simplify certificate rotation and access control.
If you operate in an environment with strict egress filtering—common in Nepali government or financial sector deployments—ensure your firewall allows outbound UDP on arbitrary ports for WireGuard, plus TCP 443 for DERP fallback. Blocked UDP forces all traffic through DERP relays, destroying performance.
How do you install and configure Headscale step by step?
The following procedure assumes Docker Compose on Ubuntu 24.04 LTS. Adapt paths and versions for your environment. Always pin explicit image tags; never use latest in production.
Create the configuration file
Create /opt/headscale/config.yaml with your core settings. This is the most error-prone step—typos here silently break client enrollment.
server_url: https://headscale.yourdomain.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
db_type: postgres
db_host: localhost
db_port: 5432
db_name: headscale
db_user: headscale
db_pass: CHANGE_ME_USE_VAULT
private_key_path: /var/lib/headscale/private.key
noise_private_key_path: /var/lib/headscale/noise_private.key
oidc:
issuer: https://auth.yourdomain.com
client_id: headscale-prod
client_secret: oidc-secret-from-vault
allowed_domains:
- yourdomain.com
strip_email_domain: true
derp:
server:
enabled: false
urls:
- https://derp.yourdomain.com/derp/map
auto_update_enabled: true
update_frequency: 24h
log:
format: json
level: info Store secrets in HashiCorp Vault or AWS Secrets Manager, not in plaintext YAML. Reference them via environment variables in your compose file. See Kubernetes secrets management done right for patterns that transfer directly to standalone deployments.
Deploy with Docker Compose
version: "3.9"
services:
headscale:
image: headscale/headscale:0.24.2
command: serve
ports:
- "8080:8080"
- "9090:9090"
volumes:
- ./config.yaml:/etc/headscale/config.yaml:ro
- headscale-data:/var/lib/headscale
environment:
- HEADSCALE_DB_PASS=${DB_PASSWORD}
restart: unless-stopped
derp:
image: fredliang/derper:latest
ports:
- "443:443"
- "3478:3478/udp"
environment:
- DERP_DOMAIN=derp.yourdomain.com
- DERP_CERT_MODE=letsencrypt
- DERP_CERT_DIR=/app/certs
volumes:
- derp-certs:/app/certs
restart: unless-stopped
volumes:
headscale-data:
derp-certs: After starting, generate the initial admin API key: docker exec headscale headscale apikeys create --expiration 365d. Store this key securely—it cannot be retrieved later.
Enroll your first node
On each client machine, run:
tailscale up --login-server=https://headscale.yourdomain.com --accept-routes This generates a node key and prints a registration URL. On the Headscale server, approve it:
headscale nodes register --user production --key <node-key-from-client-output> For automated fleet enrollment, create reusable pre-authentication keys instead of approving each node manually. This is essential for CI runners, Kubernetes nodes, or any ephemeral infrastructure where manual approval doesn’t scale.
How does Headscale compare to official Tailscale for production use?
Choosing whether to self-host Tailscale with Headscale depends on your specific constraints. This comparison reflects real operational trade-offs observed across multiple production deployments in 2026.
| Criteria | Official Tailscale | Headscale (Self-Hosted) |
|---|---|---|
| Data Sovereignty | Metadata stored on Tailscale Inc. servers | All data stays on your infrastructure |
| Setup Complexity | Zero-config, instant | Moderate: DB, TLS, DERP, OIDC required |
| High Availability | Built-in global redundancy | You design and maintain HA yourself |
| ACL Policy Engine | Full HuJSON policy with groups, tags, tests | Supported but less mature tooling |
| Admin UI | Polished web console | Community UIs exist; CLI-first by default |
| Compliance Scope | Vendor SOC 2 report accepted | Your infra enters audit scope directly |
| Cost at Scale | Per-user pricing adds up past ~50 users | Fixed infra cost; marginal cost near zero |
| Offline / Air-Gapped | Not supported | Fully functional without internet |
The verdict is straightforward: if your organization has fewer than 30 users, no regulatory constraints, and values time-to-value over sovereignty, use official Tailscale. If you handle sensitive data subject to Nepal’s data residency expectations, operate in regulated sectors, exceed 100 users, or require air-gap capability, the operational investment to self-host Tailscale with Headscale pays for itself within months.
What monitoring and maintenance practices keep Headscale reliable?
Running the software is table stakes. Keeping it reliable requires observability. Headscale exposes Prometheus metrics on port 9090. Track these signals as part of your broader four golden signals framework:
- Registration latency:
headscale_node_registration_duration_seconds. Spikes indicate database contention or TLS handshake failures. - Failed authentications:
headscale_auth_failures_total. Sudden increases signal OIDC misconfiguration or credential rotation issues. - DERP relay utilization: Monitor bandwidth and concurrent connections on your DERP nodes. High sustained usage means UDP is blocked somewhere in your network path.
- Database connection pool saturation: Headscale opens many short-lived connections during sync bursts. Set
max_open_connsappropriately and alert on pool wait time.
Back up PostgreSQL daily. Test restoration quarterly. Headscale state is small but irreplaceable—losing it means re-enrolling every node. Automate backups using the same patterns described in PostgreSQL backup and restore with pg_dump.
Upgrade Headscale during maintenance windows only. Read release notes carefully—breaking changes in the control protocol can strand nodes until they’re re-enrolled. Maintain a staging environment that mirrors production topology to validate upgrades before touching live infrastructure.
Getting Started with Your Private Mesh Network
When you self-host Tailscale with Headscale correctly, you gain a sovereign, auditable mesh VPN that scales from home labs to multinational enterprises without per-seat licensing. Start with a single-node proof of concept using Docker Compose and SQLite to validate the workflow, then migrate to PostgreSQL and redundant DERP relays before onboarding production workloads. Document your enrollment procedures, automate backups, and integrate metrics into your existing observability stack from day one. If you need help designing a compliant, production-grade Headscale deployment tailored to your infrastructure constraints, reach out to discuss your architecture.