Vom Chaos zur Kontrolle: Headscale statt Tailscale?

Vom Chaos zur Kontrolle: Headscale statt Tailscale?

Oder: Wie wir dachten, in fünf Minuten ein dezentrales VPN zu bauen – und dann fünf Stunden lang Debug-Logs lasen.


🧪 Das Experiment beginnt

Die Idee war einfach – fast schon zu einfach: Statt dem vertrauten Tailscale-Client wollten wir Headscale nutzen, das Open-Source-Gegenstück zur Verwaltung eines eigenen Tailnets.

Unsere Motivation:

  • volle Kontrolle über das Mesh-Netz
  • kein Vendor-Lock-in
  • und natürlich: nerdiger Selbstausbau einer Exit-Node in den USA

🛠️ Der Plan

  1. Headscale als Docker-Container auf dem ARM-VPS deployen
  2. Tailscale-Clients mit --login-server=https://mesh.pixli.li verbinden
  3. Zugriff über die eigene Domain mesh.pixli.li via Caddy sichern
  4. Preauth-Key erstellen, Node verbinden, Exit-Node aktivieren
  5. (Optional) Schöne UI mit headscale-ui hinzufügen

Klingt überschaubar, oder?


😵‍💫 Was schiefging – und warum

Error: fetch control key: 502 Bad Gateway  
Error: AuthKey not found  
Error: unknown flag: --user

Kleine Ursachen, große Wirkung:

  • FRISCHER API-Bruch:
    Die Headscale-Version v0.25.1 hatte mehrere Breaking Changes.
    Flags wie --wg, --user oder wg-config? Weg.
    Stattdessen neue Syntax & Subkommandos.
  • UI-Probleme:
    Die Docker-Images für headscale-ui wurden zwischenzeitlich aus dem GitHub Container Registry entfernt (403: denied), wodurch die Web-Oberfläche nicht mehr startete.
  • HTTPS-Verwirrung:
    Der Server meldete 500 Internal Server Error, wenn config.yaml auf http:// stand – obwohl TLS über Caddy lief.
    Lösung: immer https:// in server_url.
  • Preauth-Key-Lebensdauer:
    Standardmäßig 1 Stunde – zu knapp für Test-Marathons.
    Wir mussten mehrfach neue Keys generieren.
  • Tailscale-Windows-Client:
    Reagierte oft gar nicht oder mit kryptischem Timeout – vermutlich wegen --force-reauth und fehlender ACL-Konfiguration.

✅ Was jetzt funktioniert

Headscale läuft stabil als Container mit eigenem config.yaml, inklusive:

server_url: https://mesh.pixli.li
prefixes.v4: 100.64.0.0/10
dns.magic_dns: false
enable_wg_listener: true
  • Authentifizierung über Preauth-Key (--reusable, --expiration 24h)
  • Alle Dienste wieder wie vorher erreichbar – inkl. der lokalen GPT- und Flux-Bildgeneratoren.
  • Daily-Status-Report & Backups weiterhin aktiv

Exit-Node auf dem US-VPS erfolgreich aktiviert:

tailscale up --login-server=https://mesh.pixli.li --auth-key=... \
  --advertise-exit-node --hostname=us-vps --ssh

📚 Lessons learned

  • Tailscale ist komfortabel – und Headscale eine Wissenschaft.
  • Wer volle Kontrolle will, bekommt sie. Und die Verantwortung dazu.
  • Die Doku hinkt manchmal der Realität hinterher.
  • Wer updated, muss changelogs lesen.
  • Docker ist cool. Aber Configs bleiben Königsdisziplin.

Wir bleiben vorerst bei Tailscale – und basteln mit Headscale weiter in der Sandbox.


🚀 Und jetzt?

  • Einrichten eines dedizierten WireGuard-Clients mit Config-Export aus Headscale
  • Aufbau einer zweiten, redundanten Exit-Node
  • Interne Dienste per ACL absichern und Tags testen

Bleib dran – das war nur der Anfang unserer Self-Hosting-Mesh-Mission.
☕ Gebaut mit Kaffee. ⚡ Angetrieben von Neugier.


Small pixels, big ideas. ✨