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
https://chat.example.org/rtc*einschließlich WebSocket an LiveKit weiterleiten (livekit:7880).- Alles andere unter
https://chat.example.org, einschließlich des WebSockets/api/ws, an den App-Server weiterleiten (server:3000). - Die Header
X-Forwarded-ForundX-Forwarded-Protosetzen 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 Host | Overlay 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:
# 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-buildProxy auf einem anderen Host
Gilt für Nginx Proxy Manager, nginx, Traefik und andere auf einer zweiten Maschine. Was sich ändert:
- Ports öffnen: Setze in
.envden WertPROXY_BIND_IPauf die LAN- oder VPN-Adresse des Chat-Hosts (ohne Angabe: alle Schnittstellen) und starte mit dem Overlayremote-proxy.ports.yml. Erlaube danach in der Firewall des Chat-Hosts3000/tcpund7880/tcpnur für die IP des Proxy-Hosts. Beide Ports sprechen unverschlüsseltes HTTP; zwischen den Hosts sollte ein privates Netz oder VPN liegen. - Ziel im Proxy: statt
server/livekitdie IP oder der interne Hostname des Chat-Hosts mit den Ports 3000 und 7880. - Medien laufen nicht über den Proxy-Host. Browser verbinden sich für Audio direkt mit dem Chat-Host:
7882/udpund7881/tcpmü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 duLIVEKIT_NODE_IP=<öffentliche IP des Chat-Hosts>in.env. Ein Chat-Host ohne eigene öffentliche Erreichbarkeit funktioniert nicht, egal wie der Proxy eingerichtet ist. TRUSTED_PROXIES: Der App-Server sieht den Proxy-Host als Absender. Liegt dessen IP in10/8,172.16/12oder192.168/16, reicht die Voreinstellung; andernfalls ergänzt du die IP in.env. Unter Docker Desktop erscheint der Absender als Docker-Gateway172.x, ebenfalls abgedeckt.
# 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-buildnginx auf dem Host
.env:PROXY_MODE=external.TRUSTED_PROXIESkann auf der Voreinstellung bleiben (Docker-Netze und 127.0.0.1); der App-Server sieht nginx als Absender aus dem Docker-Bridge-Netz.- Starte mit dem Overlay
nginx.ports.yml(Befehle oben). - Übernimm den folgenden Server-Block, etwa nach
/etc/nginx/sites-available/chat.conf, passechat.example.orgund die Zertifikatspfade an und lade nginx neu:nginx -t && systemctl reload nginx. - Firewall:
443/tcp,80/tcp(Umleitung),7881/tcp,7882/udpöffnen.
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.
# 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-buildAlternativ ohne Overlay:
docker network connect squorli_internal <npm-container>Lege in der NPM-Oberfläche einen Proxy Host an:
| Reiter | Feld | Wert |
|---|---|---|
| Details | Domain Names | chat.example.org |
| Details | Scheme / Forward Hostname / Port | http / server / 3000 |
| Details | Websockets Support | an (Pflicht, sonst kein /api/ws) |
| Details | Cache Assets | aus |
| Details | Block Common Exploits | optional |
| Custom Locations | location /rtc | Scheme http, Forward Hostname livekit, Port 7880 |
| Custom Locations | Zahnrad bei /rtc (Advanced) | die beiden Timeout-Zeilen unten |
| SSL | Certificate | Request Let's Encrypt |
| SSL | Force SSL, HTTP/2 Support | an |
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:
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.
- Stack:
PROXY_BIND_IP=127.0.0.1(3000 und 7880 nur für den nginx des Hosts erreichbar),TRUSTED_PROXIESauf der Voreinstellung (der Container sieht nginx als Docker-Gateway172.x),LIVEKIT_NODE_IP= öffentliche IP des Plesk-Hosts. - 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:
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
}- Zertifikat: Let's Encrypt für die Domain in Plesk (SSL/TLS-Zertifikate), „Permanente SEO-sichere 301-Umleitung von HTTP zu HTTPS“ einschalten.
- Plesk-Firewall: eingehend
7881/tcpund7882/udperlauben (Medien gehen direkt zu LiveKit); 80/443 wie gewohnt. 3000 und 7880 bleiben geschlossen. - Prüfen:
https://<domain>/api/healthliefert JSON mitdomain= Plesk-Domain,https://<domain>/rtc/validateantwortet 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.
# 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-buildCaddy
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.
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
curl -s https://chat.example.org/api/health
curl -s -o /dev/null -w '%{http_code}\n' https://chat.example.org/rtc/validateDie 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/wsnicht, fehlen meistproxy_set_header UpgradeundConnection. - Verbindet sich
/rtc, aber es kommt kein Ton an, sind7882/udpund7881/tcpnicht offen oder LiveKit kennt seine öffentliche IP nicht:LIVEKIT_NODE_IPin.envsetzen. - Zeigt
/api/healtheine anderedomainals erwartet, stimmtPUBLIC_DOMAINin.envnicht mit dem Hostnamen überein, den die Browser verwenden.
Updates und Backups · Server administrieren · Overlays und nginx.conf im Repository