Headscale + Headplane¶
Headscale ist die selbst-gehostete Alternative zum Tailscale-Koordinationsserver. Er verwaltet VPN-Teilnehmer, Schluessel und Zugriffsregeln (ACLs) fuer das gesamte Homelab-Netzwerk. Headplane stellt die Web-Oberflaecche fuer die Verwaltung bereit.
Zugang¶
| Komponente | URL | Port (intern) |
|---|---|---|
| Headplane (Web-UI) | https://headscale.homelab-external.robinwerner.net/admin |
3000 |
| Headscale API | https://headscale.homelab-external.robinwerner.net |
8080 |
| STUN/DERP | headscale.homelab-external.robinwerner.net:3478 |
3478 (UDP+TCP) |
/ wird automatisch auf /admin weitergeleitet.
Container-Konfiguration¶
Headscale¶
| Parameter | Wert |
|---|---|
| Image | ghcr.io/juanfont/headscale:v0.28.0 |
| Neustart | unless-stopped |
| Netzwerke | proxy, internal |
| Abhaengigkeit | headscale-postgres (healthcheck) |
Volumes:
| Quelle | Ziel | Typ |
|---|---|---|
./headscale/config.yaml |
/etc/headscale/config.yaml |
Git-Config (ro) |
./headscale/acl.json |
/etc/headscale/acl.json |
Git-Config (ro) |
./headscale/dns_records.json |
/etc/headscale/dns_records.json |
Git-Config (ro) |
/opt/homelab-data/headscale |
/var/lib/headscale |
Persistente Daten |
Umgebungsvariablen:
| Variable | Beschreibung |
|---|---|
HEADSCALE_DATABASE_POSTGRES_PASS |
Ueberschreibt DB-Passwort aus config.yaml (aus .env) |
Headplane¶
| Parameter | Wert |
|---|---|
| Image | ghcr.io/tale/headplane:0.6.2 |
| Neustart | unless-stopped |
| Netzwerke | proxy, internal |
| Abhaengigkeit | headscale |
Volumes:
| Quelle | Ziel | Beschreibung |
|---|---|---|
./headplane/config.yaml |
/etc/headplane/config.yaml |
Headplane-Config (ro) |
./headscale/config.yaml |
/etc/headscale/config.yaml |
Headscale-Config lesen (ro) |
./headscale/acl.json |
/etc/headscale/acl.json |
ACL lesen/schreiben (ro) |
./headscale/dns_records.json |
/etc/headscale/dns_records.json |
DNS-Records (ro) |
/opt/homelab-data/headplane |
/var/lib/headplane |
Persistente Daten (SQLite) |
/opt/homelab-data/secrets/cookie_secret |
/run/secrets/cookie_secret |
Session-Secret (ro) |
/var/run/docker.sock |
/var/run/docker.sock |
Docker-Integration (ro) |
PostgreSQL (Headscale)¶
| Parameter | Wert |
|---|---|
| Image | postgres:17.9-alpine |
| Netzwerk | internal (kein Traefik-Zugriff) |
| Daten | /opt/homelab-data/headscale-postgres |
| Datenbank | headscale / User headscale |
Das DB-Passwort wird ueber POSTGRES_PASSWORD aus .env gesetzt —
derselbe Wert gilt auch fuer die Healthchecks-Datenbank.
Netzwerk¶
Headscale und Headplane teilen sich das gleiche Traefik-Routing ueber headscale.homelab-external.robinwerner.net:
- Traefik-Router
headplanemitPathPrefix(/admin)undpriority=200leitet/admin-Pfade an Headplane - Traefik-Router
headscaleleitet alle anderen Pfade an Headscale - CORS-Middleware erlaubt
Content-TypeundAuthorizationHeader (noetig fuer Headplane-API-Zugriff) - Port 3478 (STUN/DERP) wird direkt an Traefik weitergereicht (UDP + TCP)
Wichtige Konfiguration¶
Server¶
IP-Adressbereiche (Tailnet)¶
| Protokoll | Praefix | Vergabe |
|---|---|---|
| IPv4 | 100.64.0.0/10 |
sequenziell |
| IPv6 | fd7a:115c:a1e0::/48 |
sequenziell |
DERP (NAT-Traversal-Relay)¶
Eigener eingebetteter DERP-Server in der Region homelab (ID 999).
Wird automatisch allen Tailscale-Clients als bevorzugter Relay angeboten.
Zusaetzlich werden die offiziellen Tailscale-DERP-Karten geladen (taeglich aktualisiert).
DNS¶
- MagicDNS aktiviert, Base-Domain:
tailnet - Globaler Nameserver:
10.10.10.3(Pi-hole im Heimnetz)
ACL-Regeln (Zusammenfassung)¶
Die ACL-Policy liegt in headscale/acl.json (Policy v2, HuJSON-Format):
| Regel | Quelle | Ziel | Beschreibung |
|---|---|---|---|
| Vollzugriff | autogroup:member |
*:* |
Persoenliche Geraete: voller VPN-Zugriff |
| Internet | autogroup:member |
autogroup:internet:* |
Exit-Node-Nutzung |
| Monitoring | tag:server |
homelab-network:53,80,443,3000,8080,8123,9090,9100 |
Hetzner-Server → Homelab (Monitoring + DNS) |
| ICMP | * |
*:* |
Ping fuer Diagnose |
| SSH | autogroup:member |
autogroup:member, autogroup:tagged |
Tailscale SSH |
Tags:
- tag:server — Server-Nodes (Hetzner); darf Monitoring-Ports erreichen
- tag:gateway — Gateway-Nodes (NUC); darf Routen und Exit-Node automatisch freigeben
Routen aus dem Subnetz 10.10.10.0/24 mit tag:gateway werden automatisch genehmigt (autoApprovers).
Wartung¶
Ersteinrichtung nach Bootstrap¶
# 1. User anlegen
docker exec headscale headscale users create homelab
# 2. API-Key fuer Headplane erzeugen (365 Tage gueltig)
docker exec headscale headscale apikeys create --expiration 365d
# -> Key in .env eintragen: HEADSCALE_API_KEY=...
# 3. Pre-Auth-Key fuer Tailscale-Client erzeugen
docker exec headscale headscale preauthkeys create --user homelab --expiration 24h
# -> Key in .env eintragen: TS_AUTHKEY=...
# 4. Stack neu starten damit .env-Werte aktiv werden
docker compose up -d headplane tailscale
Geraete verwalten¶
# Alle Nodes anzeigen
docker exec headscale headscale nodes list
# Routen eines Nodes anzeigen
docker exec headscale headscale nodes list-routes
# Routen freigeben
docker exec headscale headscale nodes approve-routes --identifier <ID> --routes 10.10.10.0/24
# Node loeschen
docker exec headscale headscale nodes delete --identifier <ID>
Logs¶
Headscale loggt im JSON-Format auf INFO-Level.
ACL aendern¶
hetzner/headscale/acl.jsonbearbeiten (HuJSON, Policy v2)git commit && git push- Auf dem Server:
git pull && docker compose restart headscale
Headplane kann ACL-Aenderungen ueber die Web-UI durchfuehren und loest dank Docker-Integration automatisch einen Headscale-Neustart aus.
Update¶
Images sind auf konkrete Versionen gepinnt. Renovate erstellt woechtentlich PRs fuer Updates.
Nach Merge auf main wird der Stack automatisch per Cron-Job (dienstags 12:00 UTC) aktualisiert.
Versionskompatibilitaet
Headplane muss zur Headscale-Version passen. Headplane v0.6.1 funktioniert nicht mit Headscale v0.28. Immer beide Images gemeinsam aktualisieren.
Image-Tag ohne v
Das Headplane-Image auf ghcr.io verwendet keinen v-Prefix:
ghcr.io/tale/headplane:0.6.2 (nicht v0.6.2).