Squorli

Server-Dokumentation · Deutsch

Squorli hinter einem vorhandenen Reverse Proxy.

Squorli Server bringt mit Caddy einen eigenen HTTPS-Proxy mit. Läuft auf deinem Host schon nginx, Nginx Proxy Manager, Plesk, Traefik oder ein eigener Caddy, betreibst du Squorli stattdessen im Modus PROXY_MODE=external hinter diesem Proxy. Diese Seite beschreibt, was der Proxy können muss, wie er die Container erreicht und welche Einstellungen die einzelnen Proxys brauchen. Die Konfigurationsdateien und Overlays liegen im Server-Repository unter deploy/proxies.

Am einfachsten wählst du im Installationsskript „Ein Reverse Proxy auf diesem Rechner“ oder „auf einem anderen Rechner“: Es setzt PROXY_MODE=external, wählt das passende Overlay und nennt am Ende die Ziele für den Proxy; die Dateien liegen dann in /opt/squorli/deploy. Bei der Installation von Hand setzt du in .env den Wert PROXY_MODE=external und verwendest das Compose-Profil external.

Was der Proxy leisten muss

  1. https://chat.example.org/rtc* einschließlich WebSocket an LiveKit weiterleiten (livekit:7880).
  2. Alles andere unter https://chat.example.org, einschließlich des WebSockets /api/ws, an den App-Server weiterleiten (server:3000).
  3. Die Header X-Forwarded-For und X-Forwarded-Proto setzen und WebSocket-Upgrades durchreichen.

Zusätzlich müssen 7882/udp und 7881/tcp auf dem Host direkt erreichbar sein, nicht über den Proxy: Darüber laufen Audio und Video. Sobald TURN aktiv ist (siehe deploy/livekit/livekit.yaml), kommt 5349/tcp hinzu.

Unterstützt werden nur eigene Subdomains wie chat.example.org. Ein Unterpfad wie example.org/chat funktioniert nicht.

Wie der Proxy die Container erreicht

Im Modus external veröffentlicht compose.yml keine HTTP-Ports; server und livekit hängen nur am Docker-Netzwerk squorli_internal. Bis zum 28. September 2026 hieß der Dienst des App-Servers app; ein Proxy im Container, der noch auf app:3000 zeigt, funktioniert weiter, denn der Container hört auf beide Namen. Je nachdem, wo dein Proxy läuft, brauchst du eines der Overlays:

Der Proxy läuft …Lösung
auf dem Host (nginx, Apache, Caddy als Paket)Overlay nginx.ports.yml: veröffentlicht 127.0.0.1:3000 und 127.0.0.1:7880, von außen bleiben beide geschlossen.
als Container auf demselben Host (Traefik, Nginx Proxy Manager, Caddy)Den Proxy-Container an das Netzwerk squorli_internal hängen oder server/livekit an das Proxy-Netzwerk (npm.network.yml, traefik.labels.yml) und die Containernamen server und livekit als Ziele verwenden.
auf einem anderen HostOverlay remote-proxy.ports.yml: veröffentlicht 3000 und 7880 auf PROXY_BIND_IP; das Ziel im Proxy ist die IP oder der interne Hostname des Chat-Hosts. Siehe Proxy auf einem anderen Host.

Proxy auf dem Host, zum Beispiel nginx aus dem Paketmanager:

Terminal
# Run from squorli/deploy: proxy installed on this host
docker compose --env-file ../.env -f compose.yml -f proxies/nginx.ports.yml --profile external pull
docker compose --env-file ../.env -f compose.yml -f proxies/nginx.ports.yml --profile external up -d --no-build

Proxy auf einem anderen Host

Gilt für Nginx Proxy Manager, nginx, Traefik und andere auf einer zweiten Maschine. Was sich ändert:

  1. Ports öffnen: Setze in .env den Wert PROXY_BIND_IP auf die LAN- oder VPN-Adresse des Chat-Hosts (ohne Angabe: alle Schnittstellen) und starte mit dem Overlay remote-proxy.ports.yml. Erlaube danach in der Firewall des Chat-Hosts 3000/tcp und 7880/tcp nur für die IP des Proxy-Hosts. Beide Ports sprechen unverschlüsseltes HTTP; zwischen den Hosts sollte ein privates Netz oder VPN liegen.
  2. Ziel im Proxy: statt server/livekit die IP oder der interne Hostname des Chat-Hosts mit den Ports 3000 und 7880.
  3. Medien laufen nicht über den Proxy-Host. Browser verbinden sich für Audio direkt mit dem Chat-Host: 7882/udp und 7881/tcp müssen dort aus dem Internet erreichbar sein (öffentliche IP oder Portweiterleitung im Router). LiveKit muss diese öffentliche Adresse kennen: Standard ist die automatische Erkennung (use_external_ip); bei NAT oder mehreren Adressen setzt du LIVEKIT_NODE_IP=<öffentliche IP des Chat-Hosts> in .env. Ein Chat-Host ohne eigene öffentliche Erreichbarkeit funktioniert nicht, egal wie der Proxy eingerichtet ist.
  4. TRUSTED_PROXIES: Der App-Server sieht den Proxy-Host als Absender. Liegt dessen IP in 10/8, 172.16/12 oder 192.168/16, reicht die Voreinstellung; andernfalls ergänzt du die IP in .env. Unter Docker Desktop erscheint der Absender als Docker-Gateway 172.x, ebenfalls abgedeckt.
Terminal
# Run from squorli/deploy: proxy on another host; PROXY_BIND_IP is set in .env
docker compose --env-file ../.env -f compose.yml -f proxies/remote-proxy.ports.yml --profile external pull
docker compose --env-file ../.env -f compose.yml -f proxies/remote-proxy.ports.yml --profile external up -d --no-build

nginx auf dem Host

  1. .env: PROXY_MODE=external. TRUSTED_PROXIES kann auf der Voreinstellung bleiben (Docker-Netze und 127.0.0.1); der App-Server sieht nginx als Absender aus dem Docker-Bridge-Netz.
  2. Starte mit dem Overlay nginx.ports.yml (Befehle oben).
  3. Übernimm den folgenden Server-Block, etwa nach /etc/nginx/sites-available/chat.conf, passe chat.example.org und die Zertifikatspfade an und lade nginx neu: nginx -t && systemctl reload nginx.
  4. Firewall: 443/tcp, 80/tcp (Umleitung), 7881/tcp, 7882/udp öffnen.
nginx · chat.conf
map $http_upgrade $connection_upgrade { default upgrade; "" close; }

# HTTP -> HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name chat.example.org;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  server_name chat.example.org;

  # Certificate as usual, e.g. certbot:
  # ssl_certificate     /etc/letsencrypt/live/chat.example.org/fullchain.pem;
  # ssl_certificate_key /etc/letsencrypt/live/chat.example.org/privkey.pem;

  client_max_body_size 30m;   # >= MAX_UPLOAD_MB plus the form data around the file

  # LiveKit signaling (WebSocket under /rtc, validation under /rtc/validate)
  location /rtc {
    proxy_pass http://127.0.0.1:7880;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
  }

  # App server: API, WebSocket /api/ws, static web client
  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
  }
}

Die Datei nginx.conf im Repository enthält denselben Block. Läuft nginx selbst als Container, hängst du ihn an das Netzwerk squorli_internal und ersetzt 127.0.0.1:3000 durch server:3000 und 127.0.0.1:7880 durch livekit:7880.

Die Beispiele für nginx, Traefik und Caddy werden bei jeder Änderung am Server automatisch geprüft: Ein Test startet Squorli hinter dem echten Proxy und prüft durch ihn hindurch die Erreichbarkeit, den WebSocket, /rtc, die Absenderadresse, einen Upload in voller Größe und die Setup-Prüfung. Nginx Proxy Manager und Plesk sind darin nicht enthalten. Prüfe deine Installation nach dem Start mit den Befehlen unter Prüfen.

Nginx Proxy Manager

Nginx Proxy Manager (NPM) auf demselben Host läuft als Container und muss server und livekit im Docker-Netz erreichen. Starte dafür mit dem Overlay npm.network.yml und passe darin den Netzwerknamen an (docker inspect <npm-container> zeigt ihn). NPM auf einem anderen Host: Folge dem Abschnitt Proxy auf einem anderen Host und trage in der Tabelle unten statt server/livekit die IP des Chat-Hosts als Forward Hostname ein.

Terminal
# Run from squorli/deploy: Nginx Proxy Manager as a container on this host
docker compose --env-file ../.env -f compose.yml -f proxies/npm.network.yml --profile external pull
docker compose --env-file ../.env -f compose.yml -f proxies/npm.network.yml --profile external up -d --no-build

Alternativ ohne Overlay:

Terminal
docker network connect squorli_internal <npm-container>

Lege in der NPM-Oberfläche einen Proxy Host an:

ReiterFeldWert
DetailsDomain Nameschat.example.org
DetailsScheme / Forward Hostname / Porthttp / server / 3000
DetailsWebsockets Supportan (Pflicht, sonst kein /api/ws)
DetailsCache Assetsaus
DetailsBlock Common Exploitsoptional
Custom Locationslocation /rtcScheme http, Forward Hostname livekit, Port 7880
Custom LocationsZahnrad bei /rtc (Advanced)die beiden Timeout-Zeilen unten
SSLCertificateRequest Let's Encrypt
SSLForce SSL, HTTP/2 Supportan
NPM · /rtc · Advanced
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;

Achte auf die Reihenfolge: /rtc ist eine Custom Location des Proxy Hosts für chat.example.org, kein eigener Host. In älteren NPM-Versionen bekommen Custom Locations das WebSocket-Upgrade nicht automatisch; scheitert der Beitritt mit einem WebSocket-Fehler zu /rtc, ergänze im Advanced-Feld der Location:

NPM · /rtc · Advanced
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Füge proxy_http_version 1.1; nur hinzu, wenn NPM es dort nicht bereits setzt; sonst meldet nginx eine doppelte Direktive und der Host geht offline.

Firewall wie bei nginx: 443/tcp, 80/tcp, 7881/tcp, 7882/udp direkt zum Host; die Medienports laufen nicht über NPM. TRUSTED_PROXIES in .env kann auf der Voreinstellung bleiben, da NPM aus einem Docker-Netz kommt.

Plesk

Der nginx des Plesk-Hosts steht vor den Containern; das funktioniert mit dem Portainer-Stack (deploy/portainer.yml) oder der Docker-Erweiterung auf demselben Host. Verwende nicht die „Docker Proxy Rules“ von Plesk: Die erzeugten Locations tragen keine Upgrade/Connection-Header, daher scheitern /api/ws und /rtc.

  1. Stack: PROXY_BIND_IP=127.0.0.1 (3000 und 7880 nur für den nginx des Hosts erreichbar), TRUSTED_PROXIES auf der Voreinstellung (der Container sieht nginx als Docker-Gateway 172.x), LIVEKIT_NODE_IP = öffentliche IP des Plesk-Hosts.
  2. Plesk › Domain › Hosting & DNS › Apache- & nginx-Einstellungen › „Zusätzliche nginx-Anweisungen“. Die Locations sind Regex-Locations, damit sie nicht mit Plesks eigener location / kollidieren; die Reihenfolge zählt, der erste Treffer gewinnt:
Plesk · zusätzliche nginx-Anweisungen
location ~ ^/rtc {
  proxy_pass http://127.0.0.1:7880;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_read_timeout 3600s;
}
location ~ ^/api/ws {
  proxy_pass http://127.0.0.1:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_read_timeout 3600s;
}
location ~ ^/ {
  proxy_pass http://127.0.0.1:3000;
  proxy_http_version 1.1;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  client_max_body_size 30m;   # >= MAX_UPLOAD_MB plus the form data around the file
}
  1. Zertifikat: Let's Encrypt für die Domain in Plesk (SSL/TLS-Zertifikate), „Permanente SEO-sichere 301-Umleitung von HTTP zu HTTPS“ einschalten.
  2. Plesk-Firewall: eingehend 7881/tcp und 7882/udp erlauben (Medien gehen direkt zu LiveKit); 80/443 wie gewohnt. 3000 und 7880 bleiben geschlossen.
  3. Prüfen: https://<domain>/api/health liefert JSON mit domain = Plesk-Domain, https://<domain>/rtc/validate antwortet mit 401, und die Debug-Ansicht im Browser (?debug) zeigt nach dem Beitritt zu einem Sprachkanal den ICE-Pfad.

Traefik

Das Overlay traefik.labels.yml ergänzt die Router-Labels für chat.example.org (App-Server) und /rtc (LiveKit, höhere Priorität). Es erwartet Traefik in einem Docker-Netzwerk namens proxy mit dem Entrypoint websecure und dem Zertifikats-Resolver letsencrypt; passe die Namen an deine an, den Netzwerknamen auch im Label traefik.docker.network: App und LiveKit hängen in zwei Netzen, und ohne dieses Label wählt Traefik womöglich das interne, das es nicht erreicht (502/504 oder hängende Anfragen). WebSockets und X-Forwarded-For erledigt Traefik selbst, eine Größengrenze für Uploads hat es standardmäßig nicht.

Terminal
# Run from squorli/deploy: Traefik as a container on this host
docker compose --env-file ../.env -f compose.yml -f proxies/traefik.labels.yml --profile external pull
docker compose --env-file ../.env -f compose.yml -f proxies/traefik.labels.yml --profile external up -d --no-build

Caddy

Für einen Caddy, den du schon für andere Seiten betreibst. Ohne einen solchen nimmst du besser PROXY_MODE=bundled: Dann bringt Squorli seinen eigenen Caddy mit. Starte wie bei nginx auf dem Host mit dem Overlay nginx.ports.yml, übernimm den folgenden Block in dein Caddyfile, ersetze chat.example.org und lade Caddy neu (caddy reload). Zertifikat, Umleitung auf HTTPS, WebSockets und X-Forwarded-For erledigt Caddy selbst.

Caddyfile
chat.example.org {
	# LiveKit signaling (WebSocket under /rtc, validation under /rtc/validate)
	reverse_proxy /rtc* 127.0.0.1:7880

	# App server: API, WebSocket /api/ws, static web client
	reverse_proxy 127.0.0.1:3000
}

Die Datei Caddyfile.external im Repository enthält denselben Block. Läuft Caddy als Container, hängst du ihn an das Netzwerk squorli_internal und verwendest server:3000 und livekit:7880 als Ziele. Firewall und Prüfung wie bei nginx.

Squorli Directory

Der Verzeichnisdienst (Handles, Schlüssel-Backup, Authenticator, Freunde) ist ein separater Dienst auf einem eigenen Host mit eigener Domain; im Proxy ist er ein gewöhnlicher Host ohne Pfadpräfix. Dein Chat-Server braucht nur DIRECTORY_URL=https://directory.squorli.com in .env. Nichts im Server-Repository veröffentlicht oder proxyt den Dienst.

Prüfen und Fehler finden

Terminal
curl -s https://chat.example.org/api/health
curl -s -o /dev/null -w '%{http_code}\n' https://chat.example.org/rtc/validate

Die erste Antwort ist JSON mit "proxyMode":"external"; die zweite ist 401: Die Anfrage hat LiveKit erreicht, das ohne Token ablehnt. Melde dich dann im Browser an, betritt die Lobby und prüfe in der Debug-Ansicht (?debug), dass Pakete ankommen.

Schneller geht es mit squorli doctor auf dem Server oder mit Verwaltung › Server › „Verbindung prüfen“ im Client: Beide prüfen Domain, Zertifikat, WebSocket-Upgrade, /rtc, die Medienports und die vertrauenswürdigen Proxys und nennen die wahrscheinliche Ursache; die Prüfung im Client sagt außerdem, ob die Medien über UDP oder nur über TCP ankommen (Beschreibung).

  • Öffnet sich der WebSocket /api/ws nicht, fehlen meist proxy_set_header Upgrade und Connection.
  • Verbindet sich /rtc, aber es kommt kein Ton an, sind 7882/udp und 7881/tcp nicht offen oder LiveKit kennt seine öffentliche IP nicht: LIVEKIT_NODE_IP in .env setzen.
  • Zeigt /api/health eine andere domain als erwartet, stimmt PUBLIC_DOMAIN in .env nicht mit dem Hostnamen überein, den die Browser verwenden.

Updates und Backups · Server administrieren · Overlays und nginx.conf im Repository

Originaldatei öffnen