Headscale Self-Hosted Tailscale
Self-hosted Tailscale control server with Headscale, web UI, and DERP relay.
Overview
Headscale is an open-source, self-hosted implementation of the Tailscale control server that enables you to create your own mesh VPN network without relying on Tailscale's SaaS infrastructure. Originally developed as a reverse-engineered alternative to Tailscale's proprietary coordination server, Headscale provides the same zero-configuration mesh networking capabilities while maintaining full control over your network coordination and user data. The project implements the Tailscale protocol completely, allowing standard Tailscale clients to connect to your private control plane instead of Tailscale's commercial service.
This stack combines Headscale with essential supporting infrastructure to create a production-ready mesh VPN deployment. The Headscale-UI component provides a web-based management interface for monitoring connected devices, managing access controls, and visualizing your mesh network topology. The DERP relay server handles NAT traversal and encrypted relay traffic when direct peer-to-peer connections aren't possible, ensuring connectivity even in challenging network environments. NGINX serves as the reverse proxy, handling SSL termination, request routing, and providing a unified entry point for web-based management interfaces.
This configuration is ideal for organizations requiring complete control over their VPN infrastructure, privacy-conscious users who want to avoid third-party services, and enterprises with compliance requirements that prohibit external coordination servers. The combination delivers the convenience of Tailscale's mesh networking with the security and control of on-premises infrastructure, making it valuable for companies with sensitive data, regulated industries, and technical teams who need customizable VPN policies that aren't available in commercial offerings.
Key Features
- Compatible with official Tailscale clients across all platforms without modification
- DERP relay server for encrypted traffic routing when direct connections fail
- Web-based UI for device management, network visualization, and access control policies
- Support for multiple namespaces to segregate different user groups or environments
- Pre-authentication key generation for streamlined device onboarding
- Machine expiry policies and automatic cleanup of stale connections
- DNS integration with MagicDNS for seamless hostname resolution across mesh nodes
- Exit node configuration for routing internet traffic through designated mesh peers
Common Use Cases
- 1Enterprise VPN replacement requiring on-premises control and compliance with data residency regulations
- 2Development teams needing secure access to staging environments and internal services across multiple locations
- 3Homelab enthusiasts creating private mesh networks for accessing self-hosted services remotely
- 4Organizations in regulated industries where third-party VPN coordination servers are prohibited
- 5Companies requiring custom access policies and network segmentation not available in commercial Tailscale
- 6Multi-cloud deployments needing secure inter-region connectivity without exposing services to public internet
- 7Remote workforce management with granular device access controls and audit trails
Prerequisites
- Public domain name with DNS control for DERP server configuration and SSL certificate generation
- Valid SSL certificates for DERP server (port 443) and web interfaces
- Minimum 1GB RAM and 2 CPU cores for handling mesh coordination and DERP relay traffic
- Understanding of Tailscale networking concepts, ACL policies, and mesh VPN architecture
- Firewall configuration allowing inbound traffic on ports 80, 443, 3478/UDP, 8080, and 9090
- Basic knowledge of namespace management and pre-authentication key workflows
For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms
docker-compose.yml
docker-compose.yml
1services: 2 headscale: 3 image: headscale/headscale:latest4 command: serve5 volumes: 6 - ./config:/etc/headscale7 - headscale_data:/var/lib/headscale8 ports: 9 - "8080:8080"10 - "9090:9090"11 networks: 12 - headscale-net13 restart: unless-stopped1415 headscale-ui: 16 image: ghcr.io/gurucomputing/headscale-ui:latest17 environment: 18 HEADSCALE_URL: http://headscale:808019 ports: 20 - "8443:443"21 depends_on: 22 - headscale23 networks: 24 - headscale-net25 restart: unless-stopped2627 derper: 28 image: fredliang/derper:latest29 environment: 30 DERP_DOMAIN: ${DERP_DOMAIN}31 DERP_CERT_MODE: manual32 DERP_VERIFY_CLIENTS: "true"33 ports: 34 - "3478:3478/udp"35 - "443:443"36 volumes: 37 - ./certs:/app/certs:ro38 networks: 39 - headscale-net40 restart: unless-stopped4142 nginx: 43 image: nginx:alpine44 ports: 45 - "80:80"46 volumes: 47 - ./nginx.conf:/etc/nginx/nginx.conf:ro48 depends_on: 49 - headscale50 networks: 51 - headscale-net52 restart: unless-stopped5354volumes: 55 headscale_data: 5657networks: 58 headscale-net: 59 driver: bridge.env Template
.env
1# Headscale Configuration2HEADSCALE_DOMAIN=headscale.example.com34# DERP Server5DERP_DOMAIN=derp.example.com67# Database URL (optional, defaults to SQLite)8# DATABASE_URL=postgres://user:pass@host/dbUsage Notes
- 1Create namespace: docker compose exec headscale headscale namespaces create default
- 2Create auth key: docker compose exec headscale headscale preauthkeys create --namespace default
- 3Web UI at https://localhost:8443
- 4Compatible with official Tailscale clients
Individual Services(4 services)
Copy individual services to mix and match with your existing compose files.
headscale
headscale:
image: headscale/headscale:latest
command: serve
volumes:
- ./config:/etc/headscale
- headscale_data:/var/lib/headscale
ports:
- "8080:8080"
- "9090:9090"
networks:
- headscale-net
restart: unless-stopped
headscale-ui
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
environment:
HEADSCALE_URL: http://headscale:8080
ports:
- "8443:443"
depends_on:
- headscale
networks:
- headscale-net
restart: unless-stopped
derper
derper:
image: fredliang/derper:latest
environment:
DERP_DOMAIN: ${DERP_DOMAIN}
DERP_CERT_MODE: manual
DERP_VERIFY_CLIENTS: "true"
ports:
- 3478:3478/udp
- "443:443"
volumes:
- ./certs:/app/certs:ro
networks:
- headscale-net
restart: unless-stopped
nginx
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- headscale
networks:
- headscale-net
restart: unless-stopped
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 headscale:5 image: headscale/headscale:latest6 command: serve7 volumes:8 - ./config:/etc/headscale9 - headscale_data:/var/lib/headscale10 ports:11 - "8080:8080"12 - "9090:9090"13 networks:14 - headscale-net15 restart: unless-stopped1617 headscale-ui:18 image: ghcr.io/gurucomputing/headscale-ui:latest19 environment:20 HEADSCALE_URL: http://headscale:808021 ports:22 - "8443:443"23 depends_on:24 - headscale25 networks:26 - headscale-net27 restart: unless-stopped2829 derper:30 image: fredliang/derper:latest31 environment:32 DERP_DOMAIN: ${DERP_DOMAIN}33 DERP_CERT_MODE: manual34 DERP_VERIFY_CLIENTS: "true"35 ports:36 - "3478:3478/udp"37 - "443:443"38 volumes:39 - ./certs:/app/certs:ro40 networks:41 - headscale-net42 restart: unless-stopped4344 nginx:45 image: nginx:alpine46 ports:47 - "80:80"48 volumes:49 - ./nginx.conf:/etc/nginx/nginx.conf:ro50 depends_on:51 - headscale52 networks:53 - headscale-net54 restart: unless-stopped5556volumes:57 headscale_data:5859networks:60 headscale-net:61 driver: bridge62EOF6364# 2. Create the .env file65cat > .env << 'EOF'66# Headscale Configuration67HEADSCALE_DOMAIN=headscale.example.com6869# DERP Server70DERP_DOMAIN=derp.example.com7172# Database URL (optional, defaults to SQLite)73# DATABASE_URL=postgres://user:pass@host/db74EOF7576# 3. Start the services77docker compose up -d7879# 4. View logs80docker compose logs -fOne-Liner
Run this command to download and set up the recipe in one step:
terminal
1curl -fsSL https://docker.recipes/api/recipes/headscale-tailscale-stack/run | bashTroubleshooting
- Headscale API returns 'namespace not found': Create default namespace using 'headscale namespaces create default' command
- Tailscale client fails to connect with 'control server unreachable': Verify headscale container is accessible on port 8080 and DNS resolution is working
- DERP relay not functioning with connection timeouts: Check SSL certificates are properly mounted and DERP_DOMAIN environment variable matches your actual domain
- Web UI shows 'failed to fetch' errors: Confirm HEADSCALE_URL environment variable points to correct headscale service endpoint
- Devices can't establish direct connections: Ensure DERP server port 3478/UDP is open and reachable from client networks
- Machine registration fails with authentication errors: Generate new pre-auth key with proper namespace and expiry settings
Community Notes
Loading...
Loading notes...
Download Recipe Kit
Get all files in a ready-to-deploy package
Includes docker-compose.yml, .env template, README, and license
Components
headscaleheadscale-uiderpernginx
Tags
#vpn#headscale#tailscale#mesh-vpn#zero-trust
Category
Security & NetworkingAd Space
Shortcuts: C CopyF FavoriteD Download