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