Bedrud verwendet LiveKit für die Echtzeit-Video- und Audiokommunikation. LiveKit stellt den SFU-Medienserver (Selective Forwarding Unit) bereit, während Bedrud die Authentifizierung, Raumverwaltung und Admin-Kontrollen übernimmt.
Eingebettet vs. Extern
Bedrud unterstützt zwei LiveKit-Bereitstellungsmodi:
- Eingebetteter Modus (Standard): Das Backend startet und verwaltet einen LiveKit-Server-Prozess intern. Es ist keine zusätzliche Infrastruktur erforderlich – das Backend verwaltet den gesamten LiveKit-Prozess-Lebenszyklus.
- Externer Modus: Bedrud verbindet sich mit einem separaten LiveKit-Server oder -Cluster. Dies ist nützlich für horizontale Skalierung oder bei Verwendung einer verwalteten LiveKit Cloud-Instanz.
Konfiguration des externen Modus
Um einen externen LiveKit-Server zu verwenden, setzen Sie die folgenden Schlüssel in config.yaml:
livekit:
host: "wss://livekit.example.com:7880" # Client WebSocket URL (ws:// or wss://)
internalHost: "https://livekit.example.com:7880" # Server-to-server API URL
apiKey: "your-api-key"
apiSecret: "your-api-secret"
external: true # Skip embedded LiveKit startup
skipTLSVerify: false # Set true if LiveKit uses self-signed certsWenn embedded auf false gesetzt ist, überspringt Bedrud das Starten des eingebetteten LiveKit-Binaries. Der API-Schlüssel und das Secret müssen mit den Anmeldeinformationen des externen Servers übereinstimmen.
Für die Einrichtung und Konfiguration eines LiveKit-Clusters lesen Sie bitte die LiveKit-Dokumentation.
Webhook-Konfiguration (nur extern)
Bei Verwendung eines externen LiveKit-Servers müssen Sie Webhooks konfigurieren, damit LiveKit Bedrud über Teilnehmer-Trennungen und Raumschließungen informieren kann. Ohne Webhooks wird der Datenbankstatus veralten.
Endpunkt: https://<your-domain>/api/livekit/webhook
Authentifizierung: Verwendet die JWT-Signierung von LiveKit — denselben apiKey/apiSecret, den Sie oben konfiguriert haben. Kein separates Geheimnis erforderlich.
LiveKit Cloud: Einstellungen → Webhooks → Neuen Webhook erstellen. Geben Sie die Endpunkt-URL ein und wählen Sie Ihren API-Schlüssel aus.
Selbst gehostetes LiveKit: Fügen Sie zu Ihrer LiveKit-YAML-Konfiguration (z. B. livekit.yaml) hinzu:
webhook:
urls: ["https://bedrud.example.com/api/livekit/webhook"]
api_key: "your-api-key"Ohne einen ordnungsgemäß konfigurierten Webhook werden participant_disconnected- und room_finished-Ereignisse nicht an Bedrud übermittelt. Das Admin-Dashboard zeigt möglicherweise veraltete Teilnehmerdaten an, und Räume werden nicht automatisch bereinigt.
LiveKit-Versionskompatibilität
Der eingebettete LiveKit-Modus von Bedrud generiert automatisch eine kompatible Konfiguration. Wenn Sie jedoch einen externen LiveKit-Server selbst hosten, beachten Sie die bahnbrechenden Änderungen in verschiedenen LiveKit-Versionen:
- LiveKit v1.12+ hat das
tls-Feld auf oberster Ebene aus seiner Konfiguration entfernt. Wenn Sietls:in Ihrer LiveKit-YAML verwendet haben, entfernen Sie es. Konfigurieren Siehttp://fürinternalHostundws://fürhostin der Bedrud-Konfiguration. TURN TLS wird weiterhin unter dem Abschnittturn:unterstützt. - LiveKit v1.11 und früher unterstützen
tls:auf oberster Ebene. Verwenden Siehttps://fürinternalHostundwss://fürhost.
Überprüfen Sie im Zweifelsfall Ihre LiveKit-Serverversion mit livekit-server --version und konsultieren Sie die LiveKit-Versionshinweise.
Eingebettete Konfigurationserzeugung (Embedded Config Generation)
Wenn TLS im eingebetteten Modus aktiviert ist, generiert Bedrud eine temporäre LiveKit-YAML-Konfiguration (/tmp/bedrud-livekit-*.yaml) mit:
- TURN aktiviert,
domainautomatisch ausserver.hostgesetzt,udp_port: 3478,tls_port: 5349, und das Server-TLS-Zertifikat wird für TURN/TLS wiederverwendet node_ipwird aufgelöst vialivekit.nodeIP→server.host→ automatische Ausgangs-IP-Erkennungbind_addressesausgelassen (LiveKit bindet standardmäßig alle Schnittstellen)
Die temporäre Datei wird beim Beenden des LiveKit-Prozesses gelöscht. Zur Umgehung der automatischen Generierung mit einer statischen Konfiguration setzen Sie livekit.configPath oder LIVEKIT_CONFIG_PATH.
Funktionsweise
1. Raumerstellung
Wenn ein Benutzer einen Raum in Bedrud erstellt, erstellt der Server nicht sofort einen LiveKit-Raum. LiveKit-Räume werden bei Bedarf erstellt, wenn der erste Teilnehmer beitritt.
2. Join Tokens
Wenn ein Benutzer einem Meeting beitritt:
-
Das Frontend sendet eine Anfrage an
/api/room/join. -
Das Backend verifiziert, dass der Benutzer die Berechtigung hat, diesem Raum beizutreten.
-
Das Backend verwendet seinen API Key und Secret, um ein signiertes JWT (Join Token) zu generieren.
-
Das Token enthält:
- Den Raumnamen.
- Die Identität des Benutzers (Anzeigename).
- Berechtigungen – beispielsweise, ob der Benutzer Audio veröffentlichen oder seinen Bildschirm freigeben darf.
-
Das Frontend erhält dieses Token und verbindet sich direkt mit dem LiveKit-Medienport (Standard
7880).
3. Raumsteuerung (Admin)
Das Backend verwendet das LiveKit Go SDK, um administrative Aktionen durchzuführen:
- Kick: Trennt einen Teilnehmer.
- Mute: Schaltet das Mikrofon eines Teilnehmers stumm.
- Berechtigungen: Ändert in Echtzeit, was ein Teilnehmer tun kann.
Netzwerkarchitektur
- API-Port (8090/443): Verwaltet HTTP-Anfragen und WebSocket-Signalisierung für den Verbindungsaufbau.
- Medienport (7880): Verwaltet Video- und Audiodaten über WebRTC-Protokolle. ICE/TCP-Fallback verwendet Port 7881, wenn UDP blockiert ist.
- TURN-Port (3478 UDP / 5349 TLS): Leitet Medien für Clients hinter restriktiven NATs oder Firewalls weiter. Siehe TURN-Server-Leitfaden.
Für Firewall- und Port-Anforderungen siehe WebRTC-Konnektivität.
Troubleshooting
Startup & Config Crashes
| Symptom | Cause | Fix |
|---|---|---|
Container crash-loops, logs could not resolve external IP | use_external_ip: true without explicit node_ip in Docker | Set node_ip: <lan-ip> under rtc: and use_external_ip: false |
LiveKit exits with TURN domain required on v1.12+ | turn.tls_port is set but no domain or TLS cert/key | Add domain: under turn:, provide cert_file/key_file, or remove tls_port for UDP-only TURN |
field tls not found on startup | LiveKit v1.12+ removed top-level tls: config field | Remove tls: block from LiveKit YAML; use http:// / ws:// in Bedrud config |
LIVEKIT_CONFIG env var not picked up | Entrypoint doesn’t parse env var (pre-v1.7 or custom entrypoint) | LiveKit reads LIVEKIT_CONFIG natively since v1.7 — verify version; pass via --config-body only if needed |
Docker: --config-body via sh -c fails with flag provided but not defined: -c | Image entrypoint is /livekit-server directly — command gets appended, not wrapped by shell | Don’t use a shell wrapper, LIVEKIT_CONFIG env var is supported natively by the binary |
Docker Compose: $LIVEKIT_CONFIG expands to empty string | Compose substitutes $VAR from host environment, not container | Use $$LIVEKIT_CONFIG to escape docker-compose variable substitution |
YAML parsing error from LIVEKIT_CONFIG | Incorrect YAML indentation or syntax | Validate: docker run --rm -e LIVEKIT_CONFIG livekit-server --config-body "$LIVEKIT_CONFIG" |
Connectivity
| Symptom | Cause | Fix |
|---|---|---|
| Admin dashboard shows “LiveKit disconnected” | Bedrud can’t reach LiveKit HTTP API | Verify internalHost in config; run curl http://<internalHost>/ from Bedrud host; check firewall |
| Token generated but client connection times out | LiveKit WebSocket unreachable from browser | Check host in livekit: config (must be reachable by clients); verify DNS/firewall; test with wscat |
| Embedded LiveKit not starting | Missing binary or permission | Ensure internal/livekit/bin/livekit-server exists (even empty file for build); check Bedrud server logs |
| Port 7880 already in use | Another process on same port | Change livekit.port or use different Docker port mapping |
| Redis connection fails in LiveKit logs | Redis unreachable or wrong address | Verify redis.address: in LiveKit YAML; check container network connectivity |
curl http://127.0.0.1:7880 returns connection refused | LiveKit crashed during startup | Check docker logs / journalctl; look for RTC/TURN validation errors near the bottom of the log |
| Token expired before client connected | Short JWT validity window | Request a fresh token via POST /api/room/join before each connection attempt |
Media & TURN
| Symptom | Cause | Fix |
|---|---|---|
| Participants join but no audio/video | UDP port range blocked or wrong node_ip | Open UDP 50000-60000; verify node_ip is the externally reachable address |
| Clients behind NAT can’t connect | TURN not configured or ports blocked | Enable TURN; open UDP 3478 (and TCP 5349 for TLS); verify TURN domain resolves |
Could not resolve external IP on startup (non-Docker) | No STUN internet access or DNS failure | Set explicit node_ip and use_external_ip: false |
| Self-signed cert errors with external LiveKit | Bedrud’s skipTLSVerify is false | Set skipTLSVerify: true in Bedrud’s livekit: config |
| Clients connect via relay unnecessarily | node_ip is a private IP behind NAT | Set node_ip to the public IP or use use_external_ip: true with STUN access |
| TURN relay not used by clients | Direct WebRTC path is working (expected) | Check chrome://webrtc-internals — srflx candidates = direct path, no TURN needed |
Webhook & State
| Symptom | Cause | Fix |
|---|---|---|
| Database shows stale active participants after disconnect | Webhook not configured for external LiveKit | Add webhook: block to LiveKit YAML with URL https://<domain>/api/livekit/webhook |
| Participants never marked inactive | Firewall blocking webhook delivery from LiveKit to Bedrud | Check LiveKit logs for webhook delivery errors; ensure port 443/8090 is reachable from LiveKit |
| Room not cleaned up after all leave | empty_timeout / departure_timeout too high | Reduce values in LiveKit YAML room: section |
Recording (Egress)
Bedrud uses LiveKit’s RoomCompositeEgress API to record rooms as MP4 files.
Prerequisites
-
Redis — LiveKit egress requires Redis for coordinating egress workers. Without Redis,
StartRoomCompositeEgressreturns permission errors. -
Egress S3 storage (external mode only) — For external LiveKit, you must configure an
egress:section in your LiveKit YAML so recordings are stored to durable S3-compatible storage. Otherwise theFileURLis a local path unreachable from Bedrud.egress: s3: access_key: "your-s3-access-key" secret_key: "your-s3-secret-key" endpoint: "http://minio:9000" bucket: "bedrud-recordings" region: "us-east-1" force_path_style: true
Troubleshooting: Recording
| Symptom | Cause | Fix |
|---|---|---|
twirp error unauthenticated: permissions denied when starting recording | Egress JWT missing Room field in grant | Update egressAuthContext to include Room: roomName in the VideoGrant |
twirp error unauthenticated: permissions denied even with valid API key | Redis not configured for LiveKit, or egress workers unavailable | Add redis: section to LiveKit YAML; ensure Redis is healthy |
Recording starts but FileURL is a local path (e.g., /tmp/...) | No egress: S3 config — LiveKit writes to worker temp dir | Add egress.s3: block to LiveKit YAML pointing to S3-compatible storage |
process_recording job fails to download | External LK’s file URL points to inaccessible local path | Configure egress S3 so LiveKit produces S3 URLs |
See also the TURN Server Guide for TURN-specific troubleshooting, WebRTC Connectivity for STUN/ICE/firewall debugging, and Installation Troubleshooting for port/perm/setup issues.
Siehe auch
- TURN-Server-Leitfaden – TURN-Architektur, Konfiguration, TLS und Fehlerbehebung
- WebRTC-Konnektivität – vollständiger STUN/ICE/TURN/SFU-Konnektivitäts-Stack
- Architekturübersicht – vollständige Systemarchitektur