Files
homelab-optimized/hosts/synology/calypso/headscale.yaml
Gitea Mirror Bot a31121a394
Some checks failed
Documentation / Build Docusaurus (push) Failing after 9s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-03-02 21:17:14 UTC
2026-03-02 21:17:14 +00:00

229 lines
8.7 KiB
YAML

# 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