Bedrud utiliza LiveKit para gestionar la comunicación de video y audio en tiempo real. LiveKit proporciona el servidor de medios SFU (Selective Forwarding Unit), y Bedrud se encarga de la autenticación, la gestión de salas y los controles de administración.
Modo Integrado vs. Externo
Bedrud admite dos modos de despliegue de LiveKit:
- Modo Integrado (Predeterminado): El backend inicia y gestiona un proceso del servidor LiveKit internamente. No se requiere infraestructura adicional - el backend gestiona el ciclo de vida del proceso de LiveKit.
- Modo Externo: Bedrud se conecta a un servidor o clúster de LiveKit independiente. Esto es útil para escalado horizontal o cuando se utiliza una instancia gestionada de LiveKit Cloud.
Configuración del Modo Externo
Para usar un servidor LiveKit externo, establece las siguientes claves en 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 certsCuando embedded es false, Bedrud omite el inicio del binario LiveKit integrado. La API key y el secret deben coincidir con las credenciales del servidor externo.
Para la configuración y despliegue de clústeres de LiveKit, consulta la documentación de LiveKit.
Configuración de Webhook (Solo Externo)
Al usar un servidor LiveKit externo, debes configurar webhooks para que LiveKit pueda notificar a Bedrud sobre desconexiones de participantes y cierres de salas. Sin webhooks, el estado de la base de datos se volverá obsoleto.
Endpoint: https://<your-domain>/api/livekit/webhook
Autenticación: Usa la firma JWT de LiveKit — el mismo apiKey/apiSecret que configuraste arriba. No se necesita ningún secreto adicional.
LiveKit Cloud: Configuración → Webhooks → Crear nuevo webhook. Ingresa la URL del endpoint y selecciona tu clave API.
LiveKit autogestionado: Añade a tu configuración YAML de LiveKit (ej. livekit.yaml):
webhook:
urls: ["https://bedrud.example.com/api/livekit/webhook"]
api_key: "your-api-key"Sin un webhook configurado correctamente, los eventos participant_disconnected y room_finished no se entregan a Bedrud. El panel de administración puede mostrar datos de participantes obsoletos y las salas no se limpiarán automáticamente.
Compatibilidad de Versiones de LiveKit
El modo LiveKit integrado de Bedrud genera automáticamente una configuración compatible. Sin embargo, si autogestionas un servidor LiveKit externo, ten en cuenta los cambios de ruptura entre las versiones de LiveKit:
- LiveKit v1.12+ eliminó el campo
tlsde nivel superior de su configuración. Si estabas usandotls:en tu YAML de LiveKit, elimínalo. Configurahttp://parainternalHostyws://parahosten la configuración de Bedrud. TURN TLS sigue siendo compatible en la secciónturn:. - LiveKit v1.11 y anteriores admiten
tls:en el nivel superior. Usahttps://parainternalHostywss://parahost.
En caso de duda, verifica la versión de tu servidor LiveKit con livekit-server --version y consulta las notas de versión de LiveKit.
Generación de Configuración Integrada (Embedded Config Generation)
Cuando TLS está habilitado en modo integrado, Bedrud genera una configuración YAML temporal de LiveKit (/tmp/bedrud-livekit-*.yaml) con:
- TURN habilitado,
domainauto-configurado desdeserver.host,udp_port: 3478,tls_port: 5349, y el certificado TLS del servidor reutilizado para TURN/TLS node_ipresuelto mediantelivekit.nodeIP→server.host→ detección automática de IP salientebind_addressesomitido (LiveKit escucha en todas las interfaces por defecto)
El archivo temporal se elimina cuando el proceso de LiveKit termina. Para omitir la generación automática con una configuración estática, establece livekit.configPath o LIVEKIT_CONFIG_PATH.
Cómo Funciona
1. Creación de Salas
Cuando un usuario crea una sala en Bedrud, el servidor no crea una sala de LiveKit inmediatamente. Las salas de LiveKit se crean bajo demanda cuando el primer participante se une.
2. Tokens de Acceso
Cuando un usuario se une a una reunión:
-
El frontend envía una solicitud a
/api/room/join. -
El backend verifica que el usuario tiene permiso para unirse a esa sala.
-
El backend utiliza su API Key y Secret para generar un JWT firmado (Token de Acceso).
-
El token contiene:
- El nombre de la sala.
- La identidad del usuario (nombre para mostrar).
- Permisos - por ejemplo, si el usuario puede publicar audio o compartir su pantalla.
-
El frontend recibe este token y se conecta directamente al puerto de medios de LiveKit (predeterminado
7880).
3. Controles de Sala (Administración)
El backend utiliza el SDK de Go de LiveKit para realizar acciones administrativas:
- Expulsar: Desconecta a un participante.
- Silenciar: Fuerza el silenciamiento del micrófono de un participante.
- Permisos: Cambia lo que un participante puede hacer en tiempo real.
Arquitectura de Red
- Puerto de API (8090/443): Gestiona las solicitudes HTTP y la señalización WebSocket para la configuración de llamadas.
- Puerto de Medios (7880): Gestiona los datos de video y audio mediante protocolos WebRTC. El fallback ICE/TCP utiliza el puerto 7881 cuando UDP está bloqueado.
- Puerto TURN (3478 UDP / 5349 TLS): Retransmite los medios para clientes detrás de NATs restrictivos o firewalls. Consulta la Guía del Servidor TURN.
Para los requisitos de firewall y puertos, consulta Conectividad WebRTC.
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.
Consulta también
- Guía del Servidor TURN - Arquitectura TURN, configuración, TLS y solución de problemas
- Conectividad WebRTC - pila completa de conectividad STUN/ICE/TURN/SFU
- Resumen de Arquitectura - arquitectura completa del sistema