Bedrud использует LiveKit для обработки видео- и аудиосвязи в реальном времени. LiveKit предоставляет медиасервер SFU (Selective Forwarding Unit), а Bedrud обеспечивает аутентификацию, управление комнатами и административное управление.
Встроенный или внешний режим
Bedrud поддерживает два режима развёртывания LiveKit:
- Встроенный режим (по умолчанию): Бэкенд запускает и управляет процессом сервера LiveKit внутренне. Дополнительная инфраструктура не требуется - бэкенд управляет жизненным циклом процесса LiveKit.
- Внешний режим: Bedrud подключается к отдельному серверу или кластеру LiveKit. Это полезно для горизонтального масштабирования или при использовании управляемого экземпляра LiveKit Cloud.
Настройка внешнего режима
Для использования внешнего сервера LiveKit установите следующие ключи в 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 certsКогда embedded равно false, Bedrud пропускает запуск встроенного бинарного файла LiveKit. API-ключ и секрет должны совпадать с учётными данными внешнего сервера.
Для настройки кластера LiveKit и конфигурации обратитесь к документации LiveKit.
Конфигурация Webhook (только для внешнего режима)
При использовании внешнего сервера LiveKit вы обязаны настроить webhook’и, чтобы LiveKit мог уведомлять Bedrud об отключениях участников и закрытии комнат. Без webhook’ов состояние базы данных устареет.
Конечная точка: https://<your-domain>/api/livekit/webhook
Аутентификация: Использует подпись JWT LiveKit — те же apiKey/apiSecret, которые вы настроили выше. Отдельный секрет не требуется.
LiveKit Cloud: Настройки → Webhooks → Создать новый webhook. Введите URL конечной точки и выберите ваш API-ключ.
Самостоятельно размещённый LiveKit: Добавьте в вашу YAML-конфигурацию LiveKit (например, livekit.yaml):
webhook:
urls: ["https://bedrud.example.com/api/livekit/webhook"]
api_key: "your-api-key"Без правильно настроенного webhook’а события participant_disconnected и room_finished не будут доставлены в Bedrud. Панель администратора может показывать устаревшие данные об участниках, а комнаты не будут очищаться автоматически.
Совместимость версий LiveKit
Встроенный режим LiveKit в Bedrud автоматически генерирует совместимую конфигурацию. Однако, если вы самостоятельно размещаете внешний сервер LiveKit, учитывайте критические изменения между версиями LiveKit:
- LiveKit v1.12+ удалил поле
tlsверхнего уровня из своей конфигурации. Если вы использовалиtls:в вашем YAML LiveKit, удалите его. Настройтеhttp://дляinternalHostиws://дляhostв конфигурации Bedrud. TURN TLS по-прежнему поддерживается в разделеturn:. - LiveKit v1.11 и более ранние версии поддерживают
tls:на верхнем уровне. Используйтеhttps://дляinternalHostиwss://дляhost.
В случае сомнений проверьте версию вашего сервера LiveKit с помощью livekit-server --version и обратитесь к примечаниям к выпуску LiveKit.
Генерация встроенной конфигурации (Embedded Config Generation)
Когда TLS включён во встроенном режиме, Bedrud создаёт временную YAML-конфигурацию LiveKit (/tmp/bedrud-livekit-*.yaml) с:
- TURN включён,
domainавтоматически установлен изserver.host,udp_port: 3478,tls_port: 5349, сертификат TLS сервера используется повторно для TURN/TLS node_ipопределяется черезlivekit.nodeIP→server.host→ автоматическое определение исходящего IPbind_addressesопущено (LiveKit по умолчанию привязывается ко всем интерфейсам)
Временный файл удаляется при завершении процесса LiveKit. Чтобы обойти автоматическую генерацию статической конфигурацией, установите livekit.configPath или LIVEKIT_CONFIG_PATH.
Как это работает
1. Создание комнаты
Когда пользователь создаёт комнату в Bedrud, сервер не создаёт комнату LiveKit немедленно. Комнаты LiveKit создаются по требованию, когда присоединяется первый участник.
2. Токены присоединения
Когда пользователь присоединяется к встрече:
-
Фронтенд отправляет запрос на
/api/room/join. -
Бэкенд проверяет, что у пользователя есть право присоединиться к этой комнате.
-
Бэкенд использует свой API Key и Secret для генерации подписанного JWT (Join Token).
-
Токен содержит:
- Имя комнаты.
- Идентификацию пользователя (отображаемое имя).
- Разрешения - например, может ли пользователь публиковать аудио или демонстрировать экран.
-
Фронтенд получает этот токен и подключается напрямую к медиапорту LiveKit (по умолчанию
7880).
3. Управление комнатой (администрирование)
Бэкенд использует LiveKit Go SDK для выполнения административных действий:
- Исключение: Отключает участника.
- Отключение микрофона: Принудительно отключает микрофон участника.
- Разрешения: Изменяет возможности участника в реальном времени.
Сетевая архитектура
- API-порт (8090/443): Обрабатывает HTTP-запросы и WebSocket-сигнализацию для настройки звонков.
- Медиапорт (7880): Обрабатывает видео- и аудиоданные с использованием протоколов WebRTC. Резервный ICE/TCP использует порт 7881, когда UDP заблокирован.
- TURN-порт (3478 UDP / 5349 TLS): Ретранслирует медиа для клиентов за строгими NAT или файрволами. См. Руководство по TURN-серверу.
Для требований к файрволу и портам см. Подключение 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.
См. также
- Руководство по TURN-серверу - архитектура TURN, конфигурация, TLS и устранение неполадок
- Подключение WebRTC - полный стек подключения STUN/ICE/TURN/SFU
- Обзор архитектуры - полная системная архитектура