docker.recipes

Headscale Self-Hosted Tailscale

advanced

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:latest
4 command: serve
5 volumes:
6 - ./config:/etc/headscale
7 - headscale_data:/var/lib/headscale
8 ports:
9 - "8080:8080"
10 - "9090:9090"
11 networks:
12 - headscale-net
13 restart: unless-stopped
14
15 headscale-ui:
16 image: ghcr.io/gurucomputing/headscale-ui:latest
17 environment:
18 HEADSCALE_URL: http://headscale:8080
19 ports:
20 - "8443:443"
21 depends_on:
22 - headscale
23 networks:
24 - headscale-net
25 restart: unless-stopped
26
27 derper:
28 image: fredliang/derper:latest
29 environment:
30 DERP_DOMAIN: ${DERP_DOMAIN}
31 DERP_CERT_MODE: manual
32 DERP_VERIFY_CLIENTS: "true"
33 ports:
34 - "3478:3478/udp"
35 - "443:443"
36 volumes:
37 - ./certs:/app/certs:ro
38 networks:
39 - headscale-net
40 restart: unless-stopped
41
42 nginx:
43 image: nginx:alpine
44 ports:
45 - "80:80"
46 volumes:
47 - ./nginx.conf:/etc/nginx/nginx.conf:ro
48 depends_on:
49 - headscale
50 networks:
51 - headscale-net
52 restart: unless-stopped
53
54volumes:
55 headscale_data:
56
57networks:
58 headscale-net:
59 driver: bridge

.env Template

.env
1# Headscale Configuration
2HEADSCALE_DOMAIN=headscale.example.com
3
4# DERP Server
5DERP_DOMAIN=derp.example.com
6
7# Database URL (optional, defaults to SQLite)
8# DATABASE_URL=postgres://user:pass@host/db

Usage Notes

  1. 1Create namespace: docker compose exec headscale headscale namespaces create default
  2. 2Create auth key: docker compose exec headscale headscale preauthkeys create --namespace default
  3. 3Web UI at https://localhost:8443
  4. 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 file
2cat > docker-compose.yml << 'EOF'
3services:
4 headscale:
5 image: headscale/headscale:latest
6 command: serve
7 volumes:
8 - ./config:/etc/headscale
9 - headscale_data:/var/lib/headscale
10 ports:
11 - "8080:8080"
12 - "9090:9090"
13 networks:
14 - headscale-net
15 restart: unless-stopped
16
17 headscale-ui:
18 image: ghcr.io/gurucomputing/headscale-ui:latest
19 environment:
20 HEADSCALE_URL: http://headscale:8080
21 ports:
22 - "8443:443"
23 depends_on:
24 - headscale
25 networks:
26 - headscale-net
27 restart: unless-stopped
28
29 derper:
30 image: fredliang/derper:latest
31 environment:
32 DERP_DOMAIN: ${DERP_DOMAIN}
33 DERP_CERT_MODE: manual
34 DERP_VERIFY_CLIENTS: "true"
35 ports:
36 - "3478:3478/udp"
37 - "443:443"
38 volumes:
39 - ./certs:/app/certs:ro
40 networks:
41 - headscale-net
42 restart: unless-stopped
43
44 nginx:
45 image: nginx:alpine
46 ports:
47 - "80:80"
48 volumes:
49 - ./nginx.conf:/etc/nginx/nginx.conf:ro
50 depends_on:
51 - headscale
52 networks:
53 - headscale-net
54 restart: unless-stopped
55
56volumes:
57 headscale_data:
58
59networks:
60 headscale-net:
61 driver: bridge
62EOF
63
64# 2. Create the .env file
65cat > .env << 'EOF'
66# Headscale Configuration
67HEADSCALE_DOMAIN=headscale.example.com
68
69# DERP Server
70DERP_DOMAIN=derp.example.com
71
72# Database URL (optional, defaults to SQLite)
73# DATABASE_URL=postgres://user:pass@host/db
74EOF
75
76# 3. Start the services
77docker compose up -d
78
79# 4. View logs
80docker compose logs -f

One-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 | bash

Troubleshooting

  • 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

Ad Space