System architecture
Component descriptions
Headscale server
Image:headscale/headscale:v0.27.0
The core control plane server that:
- Manages device registration and authentication
- Coordinates the mesh VPN network topology
- Handles node discovery and NAT traversal via DERP
- Stores network state in PostgreSQL
- Provides REST API for management operations
- Serves health and Prometheus metrics endpoints
8080- HTTP API and control plane9090- Metrics (exposed to localhost only)50443- gRPC API
nginx reverse proxy
Image:nginx:alpine
Provides:
- SSL/TLS termination with Let’s Encrypt certificates
- HTTP to HTTPS redirection (production)
- Request routing to backend services
- Rate limiting protection
- Security headers (HSTS, CSP, X-Frame-Options)
- WebSocket connection upgrades
- Health check endpoint proxying
- Access and error logging
- Development:
8000(HTTP only) - Production:
80(HTTP redirect),443(HTTPS)
PostgreSQL database
Image:postgres:18-alpine
Persistent data storage for:
- User accounts
- Node registrations
- Pre-authentication keys
- Route advertisements
- ACL policies (when using database mode)
- Audit logs
- Health checks via
pg_isready - Data persistence with Docker volume
- Connection pooling (max 10 connections)
- Automatic dependency management (headscale waits for healthy DB)
Headplane web GUI
Image:ghcr.io/tale/headplane:latest
Web-based management interface providing:
- User management (create, list, delete)
- Node overview and management
- Pre-auth key generation
- Route approval
- ACL policy editor
- Real-time status monitoring
http://localhost:3001/admin/
Certbot
Image:certbot/certbot:latest
Automated SSL/TLS certificate management:
- Obtains Let’s Encrypt certificates
- Renews certificates automatically every 12 hours
- Uses HTTP-01 ACME challenge
- Shares certificate directory with nginx
Network architecture
Docker network
All services communicate via theheadscale-network bridge network:
headscale:8080- Headscale APIpostgres:5432- Database connectionheadplane:3000- Web GUI
Service dependencies
Dependency chain ensures proper startup order:Health checks ensure dependent services don’t start until their dependencies are fully operational.
Data persistence
Volumes
PostgreSQL data:config.yaml and policy.json.
Headscale data:
Request flow
Client connection request
-
Client initiates connection
-
nginx receives HTTPS request on port 443
- Terminates SSL/TLS
- Applies rate limiting (production)
- Adds security headers
-
nginx routes to Headscale at
headscale:8080- Proxies
/and/api/*endpoints - Enables WebSocket upgrades
- Sets proxy headers (
X-Real-IP,X-Forwarded-For)
- Proxies
-
Headscale processes request
- Validates pre-auth key
- Registers node in PostgreSQL
- Returns network configuration
-
Client establishes mesh connectivity
- Direct peer-to-peer when possible
- Via DERP relay when NAT traversal fails
Health check flow
- nginx receives request on
/health - Proxies to
headscale:8080/health(no logging) - Headscale responds with
{"status":"pass"} - nginx returns response to client
Metrics collection flow
- Prometheus-format metrics
- Node counts, API request rates, database query timing
- Not proxied through nginx for security
Development vs production
Development mode
Characteristics:- HTTP only (no SSL/TLS)
- Single port:
8000 - Uses
nginx.dev.conf(via docker-compose override) - No rate limiting
- Simplified configuration
- Localhost access only
Production mode
Characteristics:- HTTPS with Let’s Encrypt
- Dual ports:
80(redirect),443(HTTPS) - Uses
nginx.conf(full security) - 3-tier rate limiting
- Security headers (HSTS, CSP, etc.)
- Public internet access
Security architecture
Authentication layers
- API authentication: Headplane uses API keys to communicate with Headscale
- Pre-auth keys: Devices authenticate using time-limited, single-use or reusable keys
- ACL policies: Tag-based access control restricts traffic between nodes
- SSL/TLS: All external communication encrypted (production)
Network isolation
Services run in isolated bridge network:- No direct external access to PostgreSQL
- Headscale API only accessible via nginx
- Metrics endpoint restricted to localhost
- Headplane accessible only through authenticated session
Data encryption
- At rest: PostgreSQL data in Docker volume
- In transit: TLS 1.2+ with modern ciphers (production)
- Mesh traffic: WireGuard protocol with Noise key exchange
Monitoring and observability
Health checks
All services have health checks:Logging
- nginx: Access and error logs to
./logs/nginx/ - Headscale: Stdout/stderr captured by Docker
- PostgreSQL: Transaction logs in Docker volume
Metrics
Prometheus metrics onhttp://localhost:9090/metrics:
- API request counts and latency
- Database connection pool stats
- Active node count
- Route advertisement count
Related documentation
Networking
DERP servers, subnet routing, and exit nodes
File structure
Directory layout and configuration files
Docker Compose
Service definitions and orchestration
nginx configuration
Reverse proxy setup and SSL configuration
