feat(infra): migrate self-hosted backend to netralax.de

Move Supabase + LiveKit from the netralax.cloud VPS to a new netralax.de server. Adds the migration runbook (docs/), one-time move scripts (scripts/migrate/), and prod Caddy/LiveKit config templates (infra/). Repoints the desktop publish/changelog URLs and prod ops config to .de. JWT_SECRET + VAPID copied identically so already-installed clients keep working; the new server also serves the legacy .cloud hostnames.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-06-02 19:39:04 +02:00
parent 588b843904
commit 5bc30c950c
15 changed files with 1807 additions and 11 deletions
+91
View File
@@ -0,0 +1,91 @@
# ─────────────────────────────────────────────────────────────────────────────
# Caddyfile — Produktion (NEW VPS, netralax.de)
#
# Dual-Domain-Übergang (.de + .cloud):
# Bereits installierte Desktop- (Vite) und Mobile- (Expo) Clients haben die
# ALTEN Hostnamen fest in ihre Bundles eingebacken
# (supabase.netralax.cloud, livekit.netralax.cloud, update.netralax.cloud).
# Deshalb bedient dieser NEUE Server BEIDE Domains aus denselben Backends:
# - die neuen *.netralax.de Hosts für aktuelle/neue Releases
# - die legacy *.netralax.cloud Hosts NUR damit Alt-Installationen weiter
# funktionieren, bis sie sich per Auto-Update auf .de umgestellt haben.
# Voraussetzung: die .cloud-DNS-A-Records müssen auf die NEUE VPS-IP zeigen.
# Die .cloud-Blöcke dürfen NICHT entfernt werden, solange noch Alt-Clients
# im Umlauf sind — sonst brechen alle bestehenden Installationen.
#
# TLS: Automatisches HTTPS via Let's Encrypt für alle Hosts.
# WebSockets: Caddy v2 reicht Upgrade/Connection-Header bei reverse_proxy
# transparent durch — sowohl für Supabase Realtime (/realtime/v1/websocket)
# als auch für LiveKit (/rtc). KEINE websocket-Direktive nötig/vorhanden.
#
# WICHTIG: Nur der Signaling-WS (7880) und das Supabase-Gateway (Kong 8000)
# laufen über Caddy. RTC-Medien (7881/tcp, 50000-50100/udp) und coturn
# (3478, 5349/TLS, 50200-50300/udp) gehen NICHT über Caddy und müssen direkt
# in der ufw geöffnet werden. TURNS auf 5349 braucht ein EIGENES Zertifikat
# für turn.netralax.de (siehe coturn.prod.conf.example).
# ─────────────────────────────────────────────────────────────────────────────
# ─────────────────────────────────────────────────────────────────────────────
# AKTIV ab Bootstrap: die NEUEN .de-Hosts.
# Die .cloud-Legacy-Blöcke stehen weiter unten und werden ERST beim Cutover
# (Runbook §10) einkommentiert — nämlich NACHDEM die .cloud-A-Records auf die
# neue VPS-IP zeigen. Grund: stehen die .cloud-Namen schon vorher in der aktiven
# Config, scheitert Caddy wiederholt an der Let's-Encrypt-Ausstellung (DNS zeigt
# noch auf den alten Server) und läuft ins ACME-Rate-Limit (5 Fehler/Host/Stunde).
# ─────────────────────────────────────────────────────────────────────────────
# Supabase API-Gateway (Kong multiplext auth/rest/realtime/storage/functions
# + Studio). EIN reverse_proxy genügt — KEINE Routen in Caddy aufsplitten.
supabase.netralax.de {
reverse_proxy localhost:8000
}
# LiveKit Signaling-WebSocket. Caddy übernimmt den WS-Upgrade automatisch.
# CORS-Header + OPTIONS-Preflight wie auf dem alten Server (Browser/Electron-
# Clients erwarten sie beim Token-/Connect-Handshake).
livekit.netralax.de {
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "GET, POST, OPTIONS"
header Access-Control-Allow-Headers "Authorization, Content-Type"
header Access-Control-Expose-Headers "*"
@options method OPTIONS
handle @options {
respond 204
}
reverse_proxy localhost:7880
}
# electron-updater Artefakte (latest.yml + .exe + changelog.json).
# WICHTIG: docroot ist /var/www/updates (NICHT .../windows). release.mjs lädt
# nach /var/www/updates/windows/ hoch und die Clients holen unter dem URL-Pfad
# /windows/latest.yml — der Pfad-Präfix /windows/ muss also auf das Unterverzeichnis
# mappen. Mit root=/var/www/updates/windows entstünde .../windows/windows → 404.
update.netralax.de {
root * /var/www/updates
file_server
}
# ─────────────────────────────────────────────────────────────────────────────
# LEGACY .cloud-Hosts — AKTIV seit dem Cutover (DNS .cloud → neue VPS-IP).
# Liefern aus denselben Backends wie die .de-Hosts, damit bereits installierte
# Clients weiterlaufen, bis sie sich per Auto-Update auf .de umgestellt haben.
# NICHT entfernen, solange Alt-Clients im Umlauf sind.
# ─────────────────────────────────────────────────────────────────────────────
supabase.netralax.cloud {
reverse_proxy localhost:8000
}
livekit.netralax.cloud {
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "GET, POST, OPTIONS"
header Access-Control-Allow-Headers "Authorization, Content-Type"
header Access-Control-Expose-Headers "*"
@options method OPTIONS
handle @options {
respond 204
}
reverse_proxy localhost:7880
}
update.netralax.cloud {
root * /var/www/updates
file_server
}
+50
View File
@@ -0,0 +1,50 @@
# ─────────────────────────────────────────────────────────────────────────────
# coturn — Produktionskonfiguration (turnserver.conf) für turn.netralax.de
#
# coturn läuft EIGENSTÄNDIG (LiveKit-internes TURN ist deaktiviert).
# TURNS (5349/TLS) läuft NICHT über Caddy und braucht daher ein EIGENES
# TLS-Zertifikat für turn.netralax.de auf der Platte (cert/pkey unten).
#
# Zertifikat besorgen — zwei Wege:
# (a) certbot standalone (Port 80 muss frei sein, nicht von Caddy belegt):
# certbot certonly --standalone -d turn.netralax.de
# -> liefert /etc/letsencrypt/live/turn.netralax.de/{fullchain,privkey}.pem
# coturn nach Renewals neu laden (z. B. certbot --deploy-hook 'systemctl reload coturn').
# (b) Caddy-Zertifikat wiederverwenden: lasse Caddy zusätzlich turn.netralax.de
# ausstellen und kopiere/symlinke das Zert aus Caddys data-Verzeichnis
# (~/.local/share/caddy/certificates/...) an die Pfade unten. Achtung:
# coturn braucht Leserechte auf cert+pkey.
#
# ufw muss offen sein: 3478/udp+tcp, 5349/tcp (TURNS), 50200-50300/udp (Relay).
# Diese Ports gehen NICHT über Caddy.
#
# external-ip auf die ÖFFENTLICHE IP der NEUEN VPS setzen.
# lt-cred-mech-User muss zu dem passen, den mint-livekit-token / die Clients
# erwarten (Platzhalter unten ersetzen).
# ─────────────────────────────────────────────────────────────────────────────
listening-port=3478
tls-listening-port=5349
# Öffentliche IP der neuen VPS.
external-ip=141.95.34.204
# Relay-Port-Range (muss in ufw offen sein).
min-port=50200
max-port=50300
realm=netralax.de
# Long-Term-Credential-Mechanismus. User-Platzhalter ersetzen
# (Format: user=NAME:PASSWORT). Passwort z. B. via `openssl rand -hex 16`.
lt-cred-mech
user=turnuser:<REPLACE_WITH_TURN_PASSWORD>
# TLS-Material für TURNS (turn.netralax.de) — siehe Kopf-Kommentar.
cert=/etc/letsencrypt/live/turn.netralax.de/fullchain.pem
pkey=/etc/letsencrypt/live/turn.netralax.de/privkey.pem
# Härtung / Korrektheit.
fingerprint
no-multicast-peers
no-cli
@@ -0,0 +1,41 @@
# ─────────────────────────────────────────────────────────────────────────────
# LiveKit + coturn — Produktions-Compose (NEW VPS, netralax.de)
#
# Dies ist die PROD-Variante von infra/livekit/docker-compose.yml (das ist nur
# Dev: coturn läuft dort mit --no-tls/--no-dtls, ohne 5349, ohne Zertifikat).
#
# Auf den Server kopieren als /opt/livekit/docker-compose.yml und daneben:
# /opt/livekit/livekit.yaml <- infra/livekit/livekit.prod.yaml.example (Keys eintragen)
# /opt/livekit/coturn.conf <- infra/livekit/coturn.prod.conf.example (external-ip + Cert)
# Start: cd /opt/livekit && docker compose up -d && docker compose ps
#
# network_mode: host — auf einem Linux-Server ist das für WebRTC der robusteste
# Weg: die RTC-UDP-Range (50000-50100) und die TURN-Relay-Range (50200-50300)
# müssen NICHT einzeln gemappt werden, und coturn/LiveKit sehen die echten
# Quell-IPs. Welche Ports tatsächlich erreichbar sind, regelt ufw (siehe
# Runbook §6.4). Auf macOS/Docker-Desktop wird host-networking NICHT unterstützt
# — dort gilt weiterhin die Dev-Compose mit explizitem Port-Mapping.
# ─────────────────────────────────────────────────────────────────────────────
services:
livekit:
image: livekit/livekit-server:latest
restart: unless-stopped
network_mode: host
command: ["--config", "/etc/livekit.yaml"]
volumes:
- ./livekit.yaml:/etc/livekit.yaml:ro
turn:
image: coturn/coturn:4.6
restart: unless-stopped
network_mode: host
# Prod: vollständige turnserver.conf statt der Dev-CLI-Flags. Diese Datei
# aktiviert TURNS auf 5349 mit dem Zertifikat für turn.netralax.de.
command: ["-c", "/etc/coturn/turnserver.conf"]
volumes:
- ./coturn.conf:/etc/coturn/turnserver.conf:ro
# TLS-Material für turn.netralax.de. coturn.conf verweist mit
# cert=/etc/letsencrypt/live/turn.netralax.de/fullchain.pem (und privkey)
# auf genau diese Pfade — daher /etc/letsencrypt read-only einhängen.
- /etc/letsencrypt:/etc/letsencrypt:ro
+41
View File
@@ -0,0 +1,41 @@
# ─────────────────────────────────────────────────────────────────────────────
# LiveKit — Produktionskonfiguration (NEW VPS)
#
# Diese Datei ERSETZT die Dev-Werte aus infra/livekit/livekit.yaml.
# Unterschiede zur Dev-Config (WICHTIG):
# - rtc.use_external_ip: true (Dev: false)
# - KEIN rtc.node_ip: 127.0.0.1 (Dev-only — würde im Prod jeden Client
# veranlassen, Medien an seinen eigenen Loopback zu senden: Call verbindet,
# aber KEIN Audio/Video).
# - echte keys: (Platzhalter unten) statt der öffentlich bekannten devkey.
#
# Die keys: müssen EXAKT zu LIVEKIT_API_KEY / LIVEKIT_API_SECRET in
# /opt/supabase/.env passen (mint-livekit-token signiert damit). Wird nur eine
# Seite rotiert, lehnt die SFU die Tokens beim Join ab (403).
#
# ufw muss offen sein: 7880/tcp (Signaling, hinter Caddy), 7881/tcp (RTC TCP),
# 50000-50100/udp (RTC). Diese Ports außer 7880 gehen NICHT über Caddy.
#
# Kopiere diese Datei als /opt/livekit/livekit.yaml und trage echte Keys ein.
# ─────────────────────────────────────────────────────────────────────────────
port: 7880
log_level: info
rtc:
tcp_port: 7881
port_range_start: 50000
port_range_end: 50100
# Prod: öffentliche IP des Servers ankündigen (NICHT Loopback wie im Dev).
use_external_ip: true
# KEIN node_ip hier — das war dev-only (127.0.0.1) und bricht im Prod die Medien.
# Produktionsschlüssel — Platzhalter. Muss zu /opt/supabase/.env passen
# (LIVEKIT_API_KEY = der key, LIVEKIT_API_SECRET = das secret).
# Erzeugen z. B. mit: openssl rand -hex 32
keys:
APIxxxxxxxxxxxx: <REPLACE_WITH_LIVEKIT_API_SECRET>
# coturn läuft separat (siehe coturn.prod.conf.example) — eingebauter TURN aus.
turn:
enabled: false