Zurück zum Blog

Headscale VPN Integration - Self-Hosted Tailscale Controler

Headscale VPN Integration - Self-Hosted Tailscale Control Server

Headscale VPN Integration

Self-Hosted Tailscale Control Server für sichere Server-zu-Client Verbindungen

1. Überblick & Architektur

Was ist Headscale?

Headscale ist eine Open-Source-Implementierung des Tailscale Control Servers. Es ermöglicht den Betrieb eines vollständig selbst gehosteten VPN-Netzwerks basierend auf WireGuard, ohne Abhängigkeit von Tailscale's Cloud-Diensten.

Anwendungsfall: n8n zu LiteLLM Verbindung

In unserer Infrastruktur verbinden wir einen Ubuntu Server (n8n Workflow-Automation) mit einem Windows Client (LiteLLM/Ollama LLM-Stack) über ein sicheres VPN-Netzwerk.

┌─────────────────────────────────────┐         ┌─────────────────────────────────────┐
│  Ubuntu Server (ssh2.matzka.cloud)  │         │     Windows Client (Lokal)          │
│                                     │         │                                     │
│  ┌─────────────┐  ┌──────────────┐  │         │  ┌──────────────┐  ┌─────────────┐  │
│  │  Headscale  │  │   Traefik    │  │         │  │  Tailscale   │  │   Docker    │  │
│  │  v0.27.1    │──│  (Reverse    │  │         │  │  (Native)    │  │   Stack:    │  │
│  │  Port 8080  │  │   Proxy)     │  │         │  │              │  │             │  │
│  └─────────────┘  └──────────────┘  │         │  └──────────────┘  │  ┌────────┐ │  │
│         │                │          │         │         │          │  │ Caddy  │ │  │
│  ┌─────────────┐         │          │         │         │          │  │  :4000 │ │  │
│  │  Tailscale  │         │          │  ◄─────VPN─────►  │          │  └────────┘ │  │
│  │  (Client)   │         │          │         │         │          │  ┌────────┐ │  │
│  │ 100.64.0.1  │         │          │         │  100.64.0.3        │  │LiteLLM │ │  │
│  └─────────────┘         │          │         │                    │  └────────┘ │  │
│         │                │          │         │                    │  ┌────────┐ │  │
│  ┌─────────────┐         │          │         │                    │  │ Ollama │ │  │
│  │     n8n     │─────────┘          │         │                    │  │ :11434 │ │  │
│  │  Workflows  │                    │         │                    │  └────────┘ │  │
│  └─────────────┘                    │         │                    └─────────────┘  │
└─────────────────────────────────────┘         └─────────────────────────────────────┘

Datenfluss:
n8n → http://llm-client:4000 → Tailnet VPN → Windows Host → Docker Caddy → LiteLLM → Ollama
Wichtig: Tailscale läuft als nativer Windows-Dienst (nicht in Docker), damit der Traffic korrekt zum Docker-Stack geroutet wird.

Komponenten-Übersicht

Komponente Funktion Standort
Headscale VPN Control Server (koordiniert Verbindungen) Ubuntu Server
Traefik Reverse Proxy mit SSL-Terminierung Ubuntu Server
Tailscale (Server) VPN Client auf dem Server Ubuntu Server
Tailscale (Client) VPN Client (Native Windows) Windows Client
Caddy Reverse Proxy für Docker Services Windows Client
LiteLLM LLM API Gateway Windows Client
Ollama LLM Runtime (Llama 3.1 8B) Windows Client

Netzwerk-Adressen

Host Tailnet IP Hostname
Ubuntu Server 100.64.0.1 server2
Windows Client 100.64.0.3 llm-client

2. Voraussetzungen

Server-Anforderungen

  • Ubuntu 22.04+ oder Debian 12+
  • Docker & Docker Compose installiert
  • Traefik als Reverse Proxy konfiguriert
  • Domain mit DNS-Eintrag (z.B. vpn.matzka.cloud)
  • Ports: 443 (HTTPS via Traefik)

Client-Anforderungen

  • Windows 10/11 oder Linux
  • Tailscale Client installiert
  • Ausgehende HTTPS-Verbindungen erlaubt

DNS-Eintrag erstellen

Bevor Sie beginnen, erstellen Sie einen DNS A-Record:

vpn.matzka.cloud → [Server-IP]

Überprüfen Sie den DNS-Eintrag:

dig +short vpn.matzka.cloud A
# Erwartete Ausgabe: 188.245.52.235

3. Server-Installation (Headscale)

3.1 Verzeichnisstruktur erstellen

ssh root@ssh2.matzka.cloud

# Verzeichnisse anlegen
sudo mkdir -p /opt/headscale/{config,data,scripts}
cd /opt/headscale

3.2 Docker Compose Konfiguration

Erstellen Sie /opt/headscale/docker-compose.yml:

version: '3.8'

services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    restart: unless-stopped
    volumes:
      - ./config/config.yaml:/etc/headscale/config.yaml:ro
      - ./data:/var/lib/headscale
      - ./data/acls.yaml:/etc/headscale/acls.yaml:ro
    command: serve
    environment:
      - TZ=Europe/Vienna
    healthcheck:
      test: ["CMD", "headscale", "health"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - frontend
    labels:
      # Traefik aktivieren
      - "traefik.enable=true"
      - "traefik.docker.network=frontend"

      # HTTPS Router
      - "traefik.http.routers.headscale.rule=Host(`vpn.matzka.cloud`)"
      - "traefik.http.routers.headscale.entrypoints=websecure"
      - "traefik.http.routers.headscale.tls=true"
      - "traefik.http.routers.headscale.tls.certresolver=letsencrypt"
      - "traefik.http.routers.headscale.service=headscale"

      # HTTP Redirect
      - "traefik.http.routers.headscale-http.rule=Host(`vpn.matzka.cloud`)"
      - "traefik.http.routers.headscale-http.entrypoints=web"
      - "traefik.http.routers.headscale-http.middlewares=redirect-to-https@docker"

      # Service
      - "traefik.http.services.headscale.loadbalancer.server.port=8080"

      # Redirect Middleware
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"

networks:
  frontend:
    external: true

3.3 Headscale Konfiguration

Erstellen Sie /opt/headscale/config/config.yaml:

# Headscale Configuration
# Docs: https://headscale.net/ref/configuration/

# Public URL (via Traefik)
server_url: https://vpn.matzka.cloud

# Listen Adressen (intern)
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090

# Database
database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

# IP-Bereiche für Tailnet
prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

# DNS-Konfiguration
dns:
  magic_dns: true
  base_domain: matzka.internal
  nameservers:
    global:
      - 1.1.1.1
      - 8.8.8.8

# ACL Policy
policy:
  path: /etc/headscale/acls.yaml
  mode: file

# Logging
log:
  level: info
  format: text

# DERP Server (für Relay-Verbindungen)
derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  auto_update_enabled: true
  update_frequency: 24h

# Noise (WireGuard-basierte Verschlüsselung)
noise:
  private_key_path: /var/lib/headscale/noise_private.key

# Unix Socket für CLI
unix_socket: /var/lib/headscale/headscale.sock
unix_socket_permission: "0770"

# TLS wird von Traefik terminiert
tls_cert_path: ""
tls_key_path: ""

# Ephemeral Nodes
ephemeral_node_inactivity_timeout: 30m

# Update Check
disable_check_updates: false

3.4 ACL-Konfiguration (Access Control Lists)

Erstellen Sie /opt/headscale/data/acls.yaml:

{
  // Headscale ACL Policy
  // Format: HuJSON (JSON mit Kommentaren)

  // Gruppen definieren
  "groups": {
    "group:admins": ["admin@matzka.cloud"]
  },

  // Tag-Besitzer (muss user@ oder group: sein)
  "tagOwners": {
    "tag:n8n-server": ["group:admins"],
    "tag:llm-client": ["group:admins"]
  },

  // Access Control Rules
  "acls": [
    // Erlaubt allen Traffic innerhalb des Tailnets
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["*:*"]
    }
  ],

  // SSH-Regeln (optional)
  "ssh": []
}

3.5 Headscale starten

# Container starten
cd /opt/headscale
docker compose up -d

# Logs prüfen
docker logs headscale

# Erwartete Ausgabe:
# INF Starting Headscale version=v0.27.1
# INF listening and serving HTTP on: 0.0.0.0:8080

3.6 User erstellen

# User (Namespace) erstellen
docker exec headscale headscale users create matzka-cloud

# User auflisten
docker exec headscale headscale users list

# Ausgabe:
# ID | Name | Username     | Email | Created
# 1  |      | matzka-cloud |       | 2026-01-10 18:55:36

3.7 Tailscale auf Server installieren

# Tailscale installieren
curl -fsSL https://tailscale.com/install.sh | sh

# Pre-Auth Key für Server generieren (1 Stunde gültig)
docker exec headscale headscale preauthkeys create \
  --user 1 \
  --expiration 1h \
  --reusable=false

# Ausgabe: <YOUR_PREAUTH_KEY>

# Server mit Headscale verbinden
tailscale up \
  --login-server=https://vpn.matzka.cloud \
  --authkey=<YOUR_PREAUTH_KEY> \
  --accept-routes

# Status prüfen
tailscale status
# Ausgabe: 100.64.0.1  server2  matzka-cloud  linux  -

4. Traefik Integration

Warum Traefik?

  • Automatisches SSL: Let's Encrypt Zertifikate werden automatisch generiert
  • Standard-Port 443: Funktioniert auch aus restriktiven Netzwerken
  • Konsistenz: Alle Services nutzen denselben Reverse Proxy

Routing-Architektur

Internet
    │
    ▼
┌─────────────────────────────────────────────┐
│              Traefik (Port 443)             │
│                                             │
│  vpn.matzka.cloud → headscale:8080          │
│  n8n.matzka.cloud → n8n:5678                │
│  auth.matzka.cloud → authentik:9000         │
│  ...                                        │
└─────────────────────────────────────────────┘
    │
    ▼
┌─────────────────┐
│   Headscale     │
│   Port 8080     │
└─────────────────┘

Traefik Labels erklärt

Label Funktion
traefik.enable=true Aktiviert Traefik für diesen Container
traefik.docker.network=frontend Verwendet das frontend-Netzwerk
traefik.http.routers.headscale.rule Routing-Regel (Host-basiert)
traefik.http.routers.headscale.tls.certresolver Let's Encrypt Zertifikat-Resolver
traefik.http.services.headscale.loadbalancer.server.port Interner Port des Containers

5. Client-Setup (Tailscale)

5.1 Pre-Auth Key generieren

Auf dem Server ausführen:

# Pre-Auth Key für Client generieren (7 Tage gültig)
docker exec headscale headscale preauthkeys create \
  --user 1 \
  --expiration 168h \
  --reusable=false

# Ausgabe: <YOUR_PREAUTH_KEY>
Wichtig: Den Pre-Auth Key sicher übermitteln (verschlüsselte E-Mail, Signal, etc.)!

5.2 Windows: Tailscale Installation

# PowerShell als Administrator

# Download Tailscale
$installerUrl = "https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe"
Invoke-WebRequest -Uri $installerUrl -OutFile "$env:TEMP\tailscale-setup.exe"

# Installation
Start-Process -FilePath "$env:TEMP\tailscale-setup.exe" -Args "/quiet /norestart" -Wait

# Mit Headscale verbinden
cd "C:\Program Files\Tailscale"
.\tailscale.exe up `
  --login-server=https://vpn.matzka.cloud `
  --authkey=<YOUR_PREAUTH_KEY> `
  --hostname=llm-client

# Status prüfen
.\tailscale.exe status
.\tailscale.exe ip -4

5.3 Linux: Tailscale Installation

# Tailscale installieren
curl -fsSL https://tailscale.com/install.sh | sh

# Mit Headscale verbinden
sudo tailscale up \
  --login-server=https://vpn.matzka.cloud \
  --authkey=<YOUR_PREAUTH_KEY> \
  --hostname=llm-client \
  --accept-routes

# Status prüfen
tailscale status
tailscale ip -4

5.4 Server: /etc/hosts aktualisieren

Nach der Client-Verbindung die IP im Server eintragen:

# Client-IP ermitteln
docker exec headscale headscale nodes list

# /etc/hosts aktualisieren
sudo nano /etc/hosts

# Zeile hinzufügen:
100.64.0.3    llm-client litellm.matzka.cloud

# Test
ping llm-client -c 4

5.5 Windows Firewall konfigurieren

# PowerShell als Administrator

# Port 4000 (LiteLLM) erlauben
New-NetFirewallRule -DisplayName "Tailscale to LiteLLM" `
  -Direction Inbound `
  -Protocol TCP `
  -LocalPort 4000 `
  -Action Allow `
  -Profile Any

# ICMP (Ping) erlauben
New-NetFirewallRule -DisplayName "Tailscale ICMP" `
  -Direction Inbound `
  -Protocol ICMPv4 `
  -Action Allow `
  -Profile Any

# Regeln prüfen
Get-NetFirewallRule | Where-Object {$_.DisplayName -like "*Tailscale*"}

6. Administration & Verwaltung

6.1 Nodes verwalten

# Alle Nodes auflisten
docker exec headscale headscale nodes list

# Beispiel-Ausgabe:
# ID | Hostname   | Name       | User         | IP addresses  | Connected
# 1  | server2    | server2    | matzka-cloud | 100.64.0.1    | online
# 2  | llm-client | llm-client | matzka-cloud | 100.64.0.3    | online

# Node umbenennen
docker exec headscale headscale nodes rename -i 2 --name "windows-llm"

# Node löschen
docker exec headscale headscale nodes delete -i 2 --force

# Node-Details anzeigen
docker exec headscale headscale nodes show -i 1

6.2 Tags verwalten

# Tag zu Node hinzufügen
docker exec headscale headscale nodes tag -i 1 -t tag:n8n-server
docker exec headscale headscale nodes tag -i 2 -t tag:llm-client

# Tags prüfen
docker exec headscale headscale nodes list
# Tags werden in der Ausgabe angezeigt

6.3 Pre-Auth Keys verwalten

# Alle Pre-Auth Keys auflisten
docker exec headscale headscale preauthkeys list --user 1

# Neuen Key erstellen (verschiedene Optionen)
# Standard (1 Stunde, einmalig)
docker exec headscale headscale preauthkeys create --user 1

# 7 Tage gültig
docker exec headscale headscale preauthkeys create --user 1 --expiration 168h

# Wiederverwendbar (für mehrere Clients)
docker exec headscale headscale preauthkeys create --user 1 --expiration 168h --reusable

# Ephemeral (Node wird nach Disconnect gelöscht)
docker exec headscale headscale preauthkeys create --user 1 --ephemeral

# Key mit Tags
docker exec headscale headscale preauthkeys create --user 1 --tags tag:llm-client

# Key ablaufen lassen
docker exec headscale headscale preauthkeys expire --user 1 <KEY>

6.4 User verwalten

# User auflisten
docker exec headscale headscale users list

# Neuen User erstellen
docker exec headscale headscale users create development

# User umbenennen
docker exec headscale headscale users rename -i 1 --new-name production

# User löschen (VORSICHT: Löscht alle Nodes!)
docker exec headscale headscale users destroy --force <NAME>

6.5 API Keys (für externe Tools)

# API Key erstellen
docker exec headscale headscale apikeys create --expiration 90d

# API Keys auflisten
docker exec headscale headscale apikeys list

# API Key widerrufen
docker exec headscale headscale apikeys expire <PREFIX>

6.6 Routes verwalten

# Alle Routes auflisten
docker exec headscale headscale routes list

# Route aktivieren
docker exec headscale headscale routes enable -r <ROUTE_ID>

# Route deaktivieren
docker exec headscale headscale routes disable -r <ROUTE_ID>

7. ACL-Konfiguration

ACL-Grundlagen

Access Control Lists (ACLs) definieren, welche Nodes miteinander kommunizieren dürfen. Headscale verwendet das HuJSON-Format (JSON mit Kommentaren).

Restriktive ACL (Produktion)

{
  // Gruppen
  "groups": {
    "group:admins": ["admin@matzka.cloud"]
  },

  // Tag-Besitzer
  "tagOwners": {
    "tag:n8n-server": ["group:admins"],
    "tag:llm-client": ["group:admins"]
  },

  // Regeln
  "acls": [
    // n8n → LiteLLM nur auf Port 4000
    {
      "action": "accept",
      "src": ["tag:n8n-server"],
      "dst": ["tag:llm-client:4000"]
    },
    // ICMP für Monitoring
    {
      "action": "accept",
      "src": ["tag:n8n-server"],
      "dst": ["tag:llm-client:*"],
      "proto": "icmp"
    },
    // Rückweg für ICMP
    {
      "action": "accept",
      "src": ["tag:llm-client"],
      "dst": ["tag:n8n-server:*"],
      "proto": "icmp"
    }
  ]
}

Permissive ACL (Entwicklung)

{
  "acls": [
    // Alles erlauben (nur für Entwicklung!)
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["*:*"]
    }
  ]
}

ACL-Syntax Referenz

Element Beispiel Beschreibung
Wildcard * Alle Nodes
Tag tag:n8n-server Nodes mit diesem Tag
User matzka-cloud Alle Nodes des Users
Gruppe group:admins Alle Nodes der Gruppe
Port tag:web:80,443 Spezifische Ports
Port-Range tag:web:1000-2000 Port-Bereich
Alle Ports tag:web:* Alle Ports

ACL prüfen und anwenden

# ACL-Syntax prüfen
docker exec headscale headscale policy check

# Aktuelle ACL anzeigen
docker exec headscale headscale policy get

# ACL aus Datei laden
docker exec headscale headscale policy set --file /etc/headscale/acls.yaml

8. n8n Integration

HTTP Request Node Konfiguration

Für LLM Chat Completions:

Method: POST
URL: http://llm-client:4000/v1/chat/completions
Authentication: Header Auth
  Name: Authorization
  Value: Bearer sk-your-api-key-here
Body Content Type: JSON
Body:
  {
    "model": "llama3.1",
    "messages": [
      {"role": "user", "content": "{{ $json.prompt }}"}
    ],
    "max_tokens": 500
  }

Für Embeddings:

Method: POST
URL: http://llm-client:4000/v1/embeddings
Authentication: Header Auth (wie oben)
Body:
  {
    "model": "nomic-embed-text",
    "input": "{{ $json.text }}"
  }

Verfügbare Modelle

Modell Typ Verwendung
llama3.1 Chat Chat Completions, Text-Generierung
nomic-embed-text Embedding RAG, Vektorsuche

Test-Request

curl -X POST http://llm-client:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-matzka-..." \
  -d '{"model":"llama3.1","messages":[{"role":"user","content":"Hello"}],"max_tokens":50}'

9. Sicherheit

Implementierte Sicherheitsmaßnahmen

Ebene Maßnahme Status
API-Authentifizierung LiteLLM API-Key Aktiv
VPN-Verschlüsselung WireGuard (Tailscale) Aktiv
Netzwerk-ACLs Headscale ACLs Aktiv
Firewall Windows Firewall (nur VPN) Aktiv
Keine öffentliche Exposition Nur VPN-Zugriff Gesichert

API-Key Management

WICHTIG: API-Key in Produktionsumgebung unbedingt ändern!

Key-Rotation:

# 1. Neuen Key generieren
openssl rand -base64 48

# 2. In .env aktualisieren
# 3. LiteLLM neu starten
docker-compose restart litellm

# 4. In n8n Workflows aktualisieren

Windows Firewall-Regeln

# Konfigurierte Regeln:
# - Port 4000/TCP - LiteLLM API
# - Port 11434/TCP - Ollama API
# - Port 80/TCP - Caddy HTTP
# - Port 443/TCP - Caddy HTTPS
# - ICMP - Ping

# Regeln anzeigen (PowerShell)
Get-NetFirewallRule | Where-Object {$_.DisplayName -like "Docker -*"}

10. Performance

Erwartete Latenz

Route Latenz Details
n8n → VPN ~20-40ms Über Tailscale/Headscale
VPN → Docker <1ms Lokal auf Windows
Docker → LLM ~50-200ms LLM Inference Zeit
Gesamt ~100-250ms Für erste Token

Ressourcen-Nutzung

Windows Client:

  • CPU: <5% (Tailscale minimal)
  • RAM: +50MB (Tailscale Service)
  • GPU: Unverändert (Ollama nutzt GPU)

Server:

  • Headscale: ~50MB RAM, <1% CPU

Gemessene Werte (Ping-Test)

ping llm-client -c 4
# 64 bytes from llm-client (100.64.0.3): icmp_seq=1 ttl=128 time=87.3 ms
# 64 bytes from llm-client (100.64.0.3): icmp_seq=2 ttl=128 time=20.4 ms
# 64 bytes from llm-client (100.64.0.3): icmp_seq=3 ttl=128 time=20.2 ms
# 64 bytes from llm-client (100.64.0.3): icmp_seq=4 ttl=128 time=19.8 ms
# --- 0% packet loss, avg ~37ms

11. Troubleshooting

Lessons Learned: Docker Netzwerk-Isolation

Problem: Tailscale als Docker Container konnte nicht auf andere Docker Container zugreifen. Server konnte VPN-Verbindung herstellen (Ping OK), aber Port 4000 war "Connection refused".

Ursache: Docker bridge network isoliert Container. Tailscale Container hatte IP im Tailnet, aber andere Container (LiteLLM, Caddy) waren nur im Docker bridge network erreichbar.

Lösung: Umstellung auf native Windows Tailscale Installation: Tailscale läuft als Windows-Dienst (nicht in Docker), Windows Host ist direkt im Tailnet, Docker Ports sind vom Host aus erreichbar.

11.1 Headscale startet nicht

# Logs prüfen
docker logs headscale

# Häufige Fehler:
# - "Invalid Owner" → ACL-Syntax prüfen (user@, group:, tag:)
# - "database locked" → Nur ein Headscale-Prozess erlaubt
# - "bind: address already in use" → Port bereits belegt

# Konfiguration testen
docker exec headscale headscale configtest

11.2 Client kann sich nicht verbinden

# Server-seitig prüfen
docker exec headscale headscale nodes list

# DNS prüfen
dig +short vpn.matzka.cloud

# HTTPS erreichbar?
curl -I https://vpn.matzka.cloud

# Pre-Auth Key gültig?
docker exec headscale headscale preauthkeys list --user 1

# Neuen Key erstellen falls abgelaufen
docker exec headscale headscale preauthkeys create --user 1 --expiration 168h

11.3 Ping funktioniert nicht

# Beide Nodes online?
docker exec headscale headscale nodes list

# Tailscale Status auf Server
tailscale status

# Tailscale Status auf Client
tailscale status  # Linux
& "C:\Program Files\Tailscale\tailscale.exe" status  # Windows

# Firewall prüfen (Windows)
Get-NetFirewallRule | Where-Object {$_.DisplayName -like "*Tailscale*"}

# Firewall prüfen (Linux)
sudo ufw status

11.4 Port nicht erreichbar

# Vom Server testen
nc -zv llm-client 4000

# Direkt mit IP testen
nc -zv 100.64.0.3 4000

# Wenn IP funktioniert aber Hostname nicht:
cat /etc/hosts | grep llm-client

# Service auf Client läuft?
# Windows:
netstat -an | findstr 4000
# Linux:
ss -tulpn | grep 4000

# Docker-Ports prüfen
docker port <container_name>

11.5 Node wechselt IP-Adresse

# Wenn ein Node eine neue IP bekommt:

# 1. Alten Node löschen
docker exec headscale headscale nodes delete -i <ID> --force

# 2. Neuen Pre-Auth Key erstellen
docker exec headscale headscale preauthkeys create --user 1 --expiration 168h

# 3. Client neu verbinden
tailscale up --login-server=https://vpn.matzka.cloud --authkey=<NEW_KEY>

# 4. /etc/hosts aktualisieren
sudo sed -i 's/100.64.0.2/100.64.0.3/g' /etc/hosts

11.6 Headscale Logs analysieren

# Live-Logs
docker logs -f headscale

# Letzte 100 Zeilen
docker logs --tail 100 headscale

# Fehler filtern
docker logs headscale 2>&1 | grep -E "(ERR|FTL|error)"

# Verbindungsversuche
docker logs headscale 2>&1 | grep -i "register"

12. Befehlsreferenz

Headscale CLI Übersicht

Befehl Beschreibung
User-Verwaltung
users list Alle User auflisten
users create <name> Neuen User erstellen
users rename -i <id> --new-name <name> User umbenennen
users destroy <name> User löschen
Node-Verwaltung
nodes list Alle Nodes auflisten
nodes show -i <id> Node-Details anzeigen
nodes delete -i <id> Node löschen
nodes rename -i <id> --name <name> Node umbenennen
nodes tag -i <id> -t <tag> Tag zu Node hinzufügen
Pre-Auth Keys
preauthkeys list --user <id> Keys auflisten
preauthkeys create --user <id> Neuen Key erstellen
preauthkeys expire --user <id> <key> Key ablaufen lassen
Policy (ACL)
policy get Aktuelle Policy anzeigen
policy check Policy-Syntax prüfen
policy set --file <path> Policy aus Datei laden
Routes
routes list Alle Routes auflisten
routes enable -r <id> Route aktivieren
routes disable -r <id> Route deaktivieren
System
health Health-Check
version Version anzeigen
configtest Konfiguration testen

Tailscale CLI Übersicht

Befehl Beschreibung
tailscale up Verbindung herstellen
tailscale down Verbindung trennen
tailscale status Status anzeigen
tailscale ip IP-Adresse anzeigen
tailscale ping <host> Tailscale-spezifischer Ping
tailscale netcheck Netzwerk-Diagnose
tailscale debug Debug-Informationen

Schnellreferenz: Neue Verbindung einrichten

# 1. Pre-Auth Key auf Server erstellen
ssh root@ssh2.matzka.cloud
docker exec headscale headscale preauthkeys create --user 1 --expiration 168h

# 2. Auf Client verbinden
# Windows:
tailscale up --login-server=https://vpn.matzka.cloud --authkey=<KEY> --hostname=<NAME>

# Linux:
sudo tailscale up --login-server=https://vpn.matzka.cloud --authkey=<KEY> --hostname=<NAME>

# 3. Node-Liste prüfen
docker exec headscale headscale nodes list

# 4. /etc/hosts auf Server aktualisieren
echo "<TAILSCALE_IP>    <HOSTNAME>" | sudo tee -a /etc/hosts

# 5. Verbindung testen
ping <HOSTNAME>
nc -zv <HOSTNAME> <PORT>

Zusammenfassung

Erreichtes Ziel

  • Stabile VPN-Verbindung zwischen Ubuntu Server und Windows Client
  • Unabhängig von dynamischen IPs (stabile Tailnet-IPs)
  • Verschlüsselte Verbindung via WireGuard
  • Netzwerk-Zugriffskontrolle via ACLs
  • 100% Self-Hosted (keine Cloud-Abhängigkeit)

Aktuelle Konfiguration

Komponente Wert
Headscale URL https://vpn.matzka.cloud
Server IP 100.64.0.1 (server2)
Client IP 100.64.0.3 (llm-client)
LiteLLM Endpoint http://llm-client:4000

Weiterführende Links

Erstellt: 10. Januar 2026

Letzte Aktualisierung: 10. Januar 2026

Version: 1.1

Autor: matzka.cloud Infrastructure Team

Status: ✅ Produktiv im Einsatz