# Headscale - Self-Hosted Tailscale Control Server # ============================================================================= # Open-source implementation of the Tailscale control server # ============================================================================= # Deployed via: Portainer GitOps # 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 # - Low resource requirements fit well alongside existing services # # External Access: # - Configure NPM to proxy headscale.vish.gg → headscale:8080 # - OIDC auth via Authentik for user management # # Architecture: # ┌─────────────────────────────────────────────────────────────────────┐ # │ HEADSCALE SETUP │ # ├─────────────────────────────────────────────────────────────────────┤ # │ │ # │ ┌─────────────┐ ┌─────────────────────────┐ │ # │ │ Clients │ │ Calypso │ │ # │ │ │ │ │ │ # │ │ ┌─────────┐ │ HTTPS/443 │ ┌───────────────────┐ │ │ # │ │ │Tailscale│ │─────────────────────▶│ │ Nginx Proxy Mgr │ │ │ # │ │ │ Client │ │ headscale.vish.gg │ │ (SSL Term) │ │ │ # │ │ └─────────┘ │ │ └─────────┬─────────┘ │ │ # │ │ │ │ │ │ │ # │ │ ┌─────────┐ │ │ ▼ │ │ # │ │ │ Phone │ │ │ ┌───────────────────┐ │ │ # │ │ │ App │ │ │ │ Headscale │ │ │ # │ │ └─────────┘ │ │ │ :8080 │ │ │ # │ │ │ │ └─────────┬─────────┘ │ │ # │ │ ┌─────────┐ │ │ │ │ │ # │ │ │ Linux │ │ │ ▼ │ │ # │ │ │ Server │ │ │ ┌───────────────────┐ │ │ # │ │ └─────────┘ │ │ │ Authentik │ │ │ # │ └─────────────┘ │ │ (OIDC Auth) │ │ │ # │ │ └───────────────────┘ │ │ # │ └─────────────────────────┘ │ # └─────────────────────────────────────────────────────────────────────┘ configs: headscale_config: content: | # Headscale Configuration # https://headscale.net/ref/configuration/ # Server URL - must be accessible from clients server_url: https://headscale.vish.gg # Listen addresses listen_addr: 0.0.0.0:8080 metrics_listen_addr: 0.0.0.0:9090 grpc_listen_addr: 0.0.0.0:50443 grpc_allow_insecure: false # TLS disabled - handled by Nginx Proxy Manager tls_cert_path: "" tls_key_path: "" # Key paths (auto-generated if not exist) private_key_path: /var/lib/headscale/private.key noise: private_key_path: /var/lib/headscale/noise_private.key # IP address allocation for the Tailscale network # Using 100.64.0.0/10 (CGNAT range) - standard for Tailscale prefixes: v4: 100.64.0.0/10 v6: fd7a:115c:a1e0::/48 # Allocate sequential IPs prefixes: allocation: sequential # DERP (relay) servers # Using Tailscale's public DERP servers (free, reliable) # You can add custom DERP servers if needed derp: server: enabled: false # Don't run our own DERP region_id: 999 region_code: "custom" region_name: "Custom DERP" stun_listen_addr: "0.0.0.0:3478" urls: - https://controlplane.tailscale.com/derpmap/default paths: [] auto_update_enabled: true update_frequency: 24h # Disable ephemeral node inactivity timeout (keep nodes registered) ephemeral_node_inactivity_timeout: 30m # Database configuration - SQLite for simplicity database: type: sqlite sqlite: path: /var/lib/headscale/db.sqlite write_ahead_log: true # OIDC Configuration - Authentik Integration # This allows users to authenticate via Authentik SSO oidc: only_start_if_oidc_is_available: true issuer: "https://sso.vish.gg/application/o/headscale/" client_id: "CHANGE_ME_CLIENT_ID" client_secret: "REDACTED_CLIENT_SECRET" # What scopes to request scope: ["openid", "profile", "email"] # Extra parameters for auth request extra_params: domain_hint: vish.gg # Which domains/users are allowed allowed_domains: - vish.gg allowed_groups: [] allowed_users: [] # Strip @domain from username strip_email_domain: true # Token expiry expiry: 180d # Use email as username use_expiry_from_token: false # Logging configuration log: format: text level: info # Disable telemetry logtail: enabled: false randomize_client_port: false # DNS configuration for the Tailscale network dns: magic_dns: true base_domain: tail.vish.gg nameservers: global: - 1.1.1.1 - 8.8.8.8 search_domains: [] extra_records: [] # Unix socket for CLI operations unix_socket: /var/run/headscale/headscale.sock unix_socket_permission: "0770" # Policy configuration (ACLs) # Define who can access what in your network policy: mode: file path: "" # Empty = allow all (configure ACLs later) services: headscale: image: headscale/headscale:latest container_name: headscale restart: unless-stopped configs: - source: headscale_config target: /etc/headscale/config.yaml volumes: # Persistent data (keys, database, socket) - headscale-data:/var/lib/headscale - headscale-socket:/var/run/headscale ports: # Main API port - proxied via NPM - "8085:8080" # gRPC port for API access - "50443:50443" # Metrics for Prometheus - "9099:9090" command: serve networks: - headscale-net # Connect to Authentik network for OIDC - authentik-net healthcheck: test: ["CMD", "headscale", "health"] interval: 30s timeout: 10s retries: 3 start_period: 10s # Headscale UI - Web interface for management # https://github.com/gurucomputing/headscale-ui headscale-ui: image: ghcr.io/gurucomputing/headscale-ui:latest container_name: headscale-ui restart: unless-stopped environment: # URL of the Headscale API (from the UI's perspective) - HTTP_PORT=8080 ports: - "8086:8080" networks: - headscale-net depends_on: headscale: condition: service_healthy volumes: headscale-data: name: headscale-data headscale-socket: name: headscale-socket networks: headscale-net: name: headscale-net driver: bridge # External network to reach Authentik authentik-net: external: true name: authentik-net