docker.recipes

Headscale with DERP

advanced

Self-hosted Tailscale control server with DERP relay for NAT traversal.

Overview

Headscale is an open-source, self-hosted implementation of the Tailscale coordination server that enables you to run your own mesh VPN infrastructure without relying on Tailscale's SaaS platform. Originally created by Juan Font, headscale provides full control over your WireGuard-based mesh network, allowing organizations to maintain complete sovereignty over their networking infrastructure while benefiting from Tailscale's elegant architecture. The project implements the Tailscale protocol to coordinate peer-to-peer connections between devices running standard Tailscale clients. This configuration combines headscale with a custom DERP (Designated Encrypted Relay for Packets) server and headscale-ui management interface to create a comprehensive self-hosted mesh VPN solution. The DERP component serves as a relay server that facilitates connections between clients behind restrictive NATs or firewalls that prevent direct peer-to-peer communication. When direct connections fail, traffic is encrypted and relayed through your DERP server, ensuring reliable connectivity across diverse network environments. The headscale-ui component provides a web-based management interface for monitoring connected devices, managing users, and viewing network topology. This stack is ideal for privacy-conscious organizations, security teams managing distributed infrastructure, and homelab enthusiasts who want enterprise-grade mesh networking without cloud dependencies. By running your own coordination and relay infrastructure, you eliminate external dependencies, reduce latency through strategically placed DERP relays, and maintain complete audit trails of network activity. This approach is particularly valuable for organizations with strict data residency requirements or those operating in air-gapped environments.

Key Features

  • Self-hosted Tailscale coordination server with full protocol compatibility
  • Integrated DERP relay server for reliable NAT traversal and connection fallback
  • Web-based management interface for device enrollment and network visualization
  • WireGuard-based mesh networking with automatic key rotation and peer discovery
  • Multi-user namespace support with granular access control policies
  • Built-in STUN server functionality for peer-to-peer connection establishment
  • API-driven device management with support for programmatic client provisioning
  • Custom DNS resolution with MagicDNS functionality for internal service discovery

Common Use Cases

  • 1Enterprise organizations requiring complete control over mesh VPN infrastructure
  • 2Development teams connecting distributed build servers and staging environments
  • 3Security-conscious businesses avoiding third-party coordination servers
  • 4Remote work setups needing secure access to on-premises resources
  • 5Homelab enthusiasts building personal mesh networks across multiple locations
  • 6Organizations with strict data residency requirements for networking metadata
  • 7Air-gapped environments requiring isolated mesh networking capabilities

Prerequisites

  • Domain name with DNS control for DERP server configuration
  • SSL/TLS certificates for DERP server placed in ./certs directory
  • Minimum 1GB RAM for stable operation of all three components
  • Ports 80, 443, 3478/UDP, 8080, 8443, and 9090 available on host system
  • Understanding of WireGuard concepts and mesh networking principles
  • Basic knowledge of Tailscale client configuration and authentication flows

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

.env Template

.env
1# Headscale with DERP
2DERP_DOMAIN=derp.example.com
3
4# Create config/config.yaml with headscale configuration
5# See: https://github.com/juanfont/headscale/blob/main/config-example.yaml

Usage Notes

  1. 1Headscale API at http://localhost:8080
  2. 2Web UI at https://localhost:8443
  3. 3DERP relay for NAT traversal
  4. 4Create users: headscale users create <name>
  5. 5Generate auth keys for clients

Individual Services(3 services)

Copy individual services to mix and match with your existing compose files.

headscale
headscale:
  image: headscale/headscale:latest
  container_name: headscale
  command: serve
  volumes:
    - ./config:/etc/headscale
    - headscale-data:/var/lib/headscale
  ports:
    - "8080:8080"
    - "9090:9090"
  networks:
    - headscale-network
  restart: unless-stopped
headscale-ui
headscale-ui:
  image: ghcr.io/gurucomputing/headscale-ui:latest
  container_name: headscale-ui
  environment:
    - HEADSCALE_URL=http://headscale:8080
  ports:
    - "8443:443"
  depends_on:
    - headscale
  networks:
    - headscale-network
  restart: unless-stopped
derper
derper:
  image: ghcr.io/tailscale/derper:latest
  container_name: derper
  environment:
    - DERP_DOMAIN=${DERP_DOMAIN}
    - DERP_CERT_MODE=manual
    - DERP_ADDR=:443
    - DERP_HTTP_PORT=80
    - DERP_STUN_PORT=3478
    - DERP_VERIFY_CLIENTS=false
  volumes:
    - ./certs:/app/certs:ro
  ports:
    - "443:443"
    - "80:80"
    - 3478:3478/udp
  networks:
    - headscale-network
  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 container_name: headscale
7 command: serve
8 volumes:
9 - ./config:/etc/headscale
10 - headscale-data:/var/lib/headscale
11 ports:
12 - "8080:8080"
13 - "9090:9090"
14 networks:
15 - headscale-network
16 restart: unless-stopped
17
18 headscale-ui:
19 image: ghcr.io/gurucomputing/headscale-ui:latest
20 container_name: headscale-ui
21 environment:
22 - HEADSCALE_URL=http://headscale:8080
23 ports:
24 - "8443:443"
25 depends_on:
26 - headscale
27 networks:
28 - headscale-network
29 restart: unless-stopped
30
31 derper:
32 image: ghcr.io/tailscale/derper:latest
33 container_name: derper
34 environment:
35 - DERP_DOMAIN=${DERP_DOMAIN}
36 - DERP_CERT_MODE=manual
37 - DERP_ADDR=:443
38 - DERP_HTTP_PORT=80
39 - DERP_STUN_PORT=3478
40 - DERP_VERIFY_CLIENTS=false
41 volumes:
42 - ./certs:/app/certs:ro
43 ports:
44 - "443:443"
45 - "80:80"
46 - "3478:3478/udp"
47 networks:
48 - headscale-network
49 restart: unless-stopped
50
51volumes:
52 headscale-data:
53
54networks:
55 headscale-network:
56 driver: bridge
57EOF
58
59# 2. Create the .env file
60cat > .env << 'EOF'
61# Headscale with DERP
62DERP_DOMAIN=derp.example.com
63
64# Create config/config.yaml with headscale configuration
65# See: https://github.com/juanfont/headscale/blob/main/config-example.yaml
66EOF
67
68# 3. Start the services
69docker compose up -d
70
71# 4. View logs
72docker 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-derp/run | bash

Troubleshooting

  • DERP server fails to start: Verify SSL certificates exist in ./certs directory with correct permissions and valid domain names
  • Clients cannot connect through DERP relay: Check DERP_DOMAIN environment variable matches certificate common name and DNS resolution
  • headscale-ui shows connection refused: Ensure headscale service is fully started before UI attempts connection, check network connectivity between containers
  • Device registration fails with authentication errors: Verify pre-auth keys are generated correctly using headscale CLI and haven't expired
  • Peer-to-peer connections not establishing: Confirm STUN server is accessible on port 3478/UDP and firewall allows WireGuard traffic
  • API requests timing out: Increase headscale container memory allocation and check for resource constraints in container logs

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