Bedrud 使用 LiveKit 处理实时视频和音频通信。LiveKit 提供 SFU(选择性转发单元)媒体服务器,Bedrud 负责认证、房间管理和管控功能。
内嵌模式 vs 外部模式
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
身份验证: 使用 LiveKit 的 JWT 签名——即上面配置的同一个 apiKey/apiSecret。无需单独的密钥。
LiveKit Cloud: 设置 → Webhooks → 创建新的 webhook。输入端点 URL 并选择您的 API 密钥。
自托管 LiveKit: 添加到您的 LiveKit YAML 配置(例如 livekit.yaml):
webhook:
urls: ["https://bedrud.example.com/api/livekit/webhook"]
api_key: "your-api-key"如果没有正确配置 webhook,participant_disconnected 和 room_finished 事件将无法传递给 Bedrud。管理面板可能会显示过时的参与者数据,房间也不会自动清理。
LiveKit 版本兼容性
Bedrud 的内嵌 LiveKit 模式会自动生成兼容的配置。但是,如果您自托管外部 LiveKit 服务器,请注意 LiveKit 各版本之间的破坏性变更:
- LiveKit v1.12+ 已从其配置中移除了顶层
tls字段。如果您在 LiveKit YAML 中使用了tls:,请删除它。在 Bedrud 配置中为internalHost配置http://,为host配置ws://。TURN TLS 在turn:部分仍然受支持。 - LiveKit v1.11 及更早版本 在顶层支持
tls:。请为internalHost使用https://,为host使用wss://。
如有疑问,请使用 livekit-server --version 检查您的 LiveKit 服务器版本,并查阅 LiveKit 发布说明。
嵌入式配置生成 (Embedded Config Generation)
当在嵌入式模式下启用 TLS 时,Bedrud 会生成一个临时的 LiveKit YAML 配置文件(/tmp/bedrud-livekit-*.yaml):
- 启用 TURN,
domain从server.host自动设置,udp_port: 3478,tls_port: 5349,服务器 TLS 证书被重复用于 TURN/TLS node_ip通过livekit.nodeIP→server.host→ 出站 IP 自动检测的优先级解析bind_addresses被省略(LiveKit 默认绑定所有接口)
临时文件在 LiveKit 进程退出时被清理。要使用静态配置绕过自动生成,请设置 livekit.configPath 或 LIVEKIT_CONFIG_PATH。
工作原理
1. 房间创建
当用户在 Bedrud 中创建房间时,服务器不会立即创建 LiveKit 房间。LiveKit 房间在第一个参与者加入时按需创建。
2. 加入令牌
当用户加入会议时:
-
前端向
/api/room/join发送请求。 -
后端验证用户是否有权加入该房间。
-
后端使用其 API Key 和 Secret 生成签名的 JWT(加入令牌)。
-
令牌包含:
- 房间名称。
- 用户的身份标识(显示名称)。
- 权限—例如用户是否可以发布音频或共享屏幕。
-
前端接收此令牌并直接连接到 LiveKit 媒体端口(默认
7880)。
3. 房间控制(管理员)
后端使用 LiveKit Go SDK 执行管理操作:
- 踢出: 断开参与者的连接。
- 静音: 强制静音参与者的麦克风。
- 权限: 实时更改参与者的可操作权限。
网络架构
- API 端口(8090/443): 处理 HTTP 请求和用于通话建立的 WebSocket 信令。
- 媒体端口(7880): 使用 WebRTC 协议处理视频和音频数据。当 UDP 被阻止时,ICE/TCP 回退使用端口 7881。
- 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 连接栈
- 架构概览 - 完整的系统架构