# Headscale - Self-Hosted Tailscale Control Server # ============================================================================= # Open-source implementation of the Tailscale control server # ============================================================================= # Deployed via: Portainer GitOps (or docker compose up -d on Calypso) # Ports: 8080 (HTTP API), 443 (HTTPS via NPM), 9090 (Metrics) # # Why Calypso? # - Already runs Authentik (SSO/OIDC provider) for seamless integration # - Already runs Nginx Proxy Manager for external HTTPS access # - Infrastructure services host (Gitea, NPM, Authentik) # - Synology NAS = always-on, stable, reliable # # External Access: # - NPM proxy host: headscale.vish.gg → 192.168.0.250:8085 # WebSocket support MUST be enabled in NPM (already configured, host ID 44) # - /admin path routed to Headplane at 192.168.0.250:3002 via NPM Advanced tab # - OIDC auth via Authentik (provider pk=15, app slug=headscale) # Authentik reached via public HTTPS - no shared Docker network needed # # Config files: # - Headscale: /volume1/docker/headscale/config/config.yaml on Calypso # - Headplane: /volume1/docker/headscale/headplane/config.yaml on Calypso # - NOT managed by inline configs block (Synology Docker Compose v2.20 doesn't support it) # # Architecture: # ┌─────────────────────────────────────────────────────────────────────┐ # │ HEADSCALE SETUP │ # ├─────────────────────────────────────────────────────────────────────┤ # │ │ # │ ┌─────────────┐ ┌─────────────────────────┐ │ # │ │ Clients │ │ Calypso │ │ # │ │ │ │ │ │ # │ │ ┌─────────┐ │ HTTPS/443 │ ┌───────────────────┐ │ │ # │ │ │Tailscale│ │─────────────────────▶│ │ Nginx Proxy Mgr │ │ │ # │ │ │ Client │ │ headscale.vish.gg │ │ (SSL Term) │ │ │ # │ │ └─────────┘ │ │ └─────────┬─────────┘ │ │ # │ │ │ │ │ │ │ # │ │ ┌─────────┐ │ │ ▼ │ │ # │ │ │ Phone │ │ │ ┌───────────────────┐ │ │ # │ │ │ App │ │ │ │ Headscale │ │ │ # │ │ └─────────┘ │ │ │ :8080 │ │ │ # │ │ │ │ └─────────┬─────────┘ │ │ # │ │ ┌─────────┐ │ │ │ │ │ # │ │ │ Linux │ │ │ ▼ │ │ # │ │ │ Server │ │ │ ┌───────────────────┐ │ │ # │ │ └─────────┘ │ │ │ Authentik │ │ │ # │ └─────────────┘ │ │ sso.vish.gg │ │ │ # │ │ │ (OIDC via HTTPS) │ │ │ # │ │ └───────────────────┘ │ │ # │ └─────────────────────────┘ │ # └─────────────────────────────────────────────────────────────────────┘ services: headscale: image: headscale/headscale:latest container_name: headscale restart: unless-stopped labels: # Required so Headplane can locate this container via Docker socket me.tale.headplane.target: "headscale" volumes: # Config file at /volume1/docker/headscale/config/config.yaml - /volume1/docker/headscale/config:/etc/headscale # Persistent data: keys, SQLite database - headscale-data:/var/lib/headscale # Unix socket for headscale CLI - headscale-socket:/var/run/headscale ports: - "8085:8080" # Main API - proxied via NPM to headscale.vish.gg - "50443:50443" # gRPC - "9099:9090" # Prometheus metrics command: serve networks: - headscale-net healthcheck: test: ["CMD", "headscale", "health"] interval: 30s timeout: 10s retries: 3 start_period: 10s headplane: image: ghcr.io/tale/headplane:latest container_name: headplane restart: unless-stopped ports: - "3002:3000" # Host port 3002 (3000+3001 taken by DSM nginx) volumes: # Headplane config (secrets live on Calypso, reference copy in repo) - /volume1/docker/headscale/headplane/config.yaml:/etc/headplane/config.yaml # Persistent data: session DB, agent cache - headplane-data:/var/lib/headplane # Shared read/write access to headscale config (for Settings UI) - /volume1/docker/headscale/config/config.yaml:/etc/headscale/config.yaml # Docker socket - read-only, needed to restart headscale after config changes - /var/run/docker.sock:/var/run/docker.sock:ro networks: - headscale-net depends_on: - headscale healthcheck: test: ["CMD", "/bin/hp_healthcheck"] interval: 30s timeout: 5s retries: 3 start_period: 10s volumes: headscale-data: name: headscale-data headscale-socket: name: headscale-socket headplane-data: name: headplane-data networks: headscale-net: name: headscale-net driver: bridge