Bedrud ドキュメント

Bedrudはリアルタイムのビデオ・オーディオ通信にLiveKitを使用しています。LiveKitがSFU(Selective Forwarding Unit)メディアサーバーを提供し、Bedrudは認証、ルーム管理、管理者コントロールを処理します。

埋め込みモードと外部モード

Bedrudは2つのLiveKitデプロイモードをサポートしています:

  1. 埋め込みモード(デフォルト): バックエンドが内部的にLiveKitサーバープロセスを起動・管理します。追加のインフラストラクチャは不要です - バックエンドがLiveKitプロセスのライフサイクルを管理します。
  2. 外部モード: 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

embeddedfalseの場合、Bedrudは組み込みLiveKitバイナリの起動をスキップします。APIキーとシークレットは外部サーバーの認証情報と一致する必要があります。

LiveKitクラスターのセットアップと設定については、LiveKitドキュメントを参照してください。

Webhookの設定(外部モードのみ)

外部LiveKitサーバーを使用する場合、LiveKitが参加者の切断やルームのクローズをBedrudに通知できるように、Webhookを必ず設定する必要があります。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有効、domainserver.hostから自動設定、udp_port: 3478tls_port: 5349、サーバーのTLS証明書をTURN/TLSに再利用
  • node_iplivekit.nodeIPserver.host → 発信IP自動検出の順に解決
  • bind_addressesは省略(LiveKitはデフォルトですべてのインターフェースにバインド)

一時ファイルはLiveKitプロセス終了時に削除されます。静的設定で自動生成を回避するには、livekit.configPathまたはLIVEKIT_CONFIG_PATHを設定してください。

仕組み

1. ルーム作成

ユーザーがBedrudでルームを作成しても、サーバーは直ちにLiveKitルームを作成しません。LiveKitルームは最初の参加者が参加した時にオンデマンドで作成されます。

2. Join Token

ユーザーがミーティングに参加する際:

  1. フロントエンドが/api/room/joinにリクエストを送信します。

  2. バックエンドはユーザーがそのルームに参加する権限を持っているか検証します。

  3. バックエンドはAPIキーとシークレットを使用して署名付きJWT(Join Token)を生成します。

  4. トークンには以下が含まれます:

    • ルーム名。
    • ユーザーのアイデンティティ(表示名)。
    • 権限 - 例えば、ユーザーが音声を配信できるか、画面共有できるか。
  5. フロントエンドはこのトークンを受け取り、LiveKitメディアポート(デフォルト7880)に直接接続します。

3. ルームコントロール(管理者)

バックエンドはLiveKit Go SDKを使用して管理操作を実行します:

  • キック: 参加者を切断します。
  • ミュート: 参加者のマイクを強制的にミュートします。
  • 権限: 参加者の権限をリアルタイムで変更します。

ネットワークアーキテクチャ

  • APIポート (8090/443): HTTPリクエストと通話セットアップ用のWebSocketシグナリングを処理します。
  • メディアポート (7880): WebRTCプロトコルを使用してビデオ・オーディオデータを処理します。ICE/TCPフォールバックはUDPがブロックされた場合にポート7881を使用します。
  • TURNポート (3478 UDP / 5349 TLS): 制限的なNATやファイアウォールの背後にあるクライアントのメディアをリレーします。TURNサーバーガイドを参照してください。

ファイアウォールとポートの要件については、WebRTC接続性を参照してください。

Troubleshooting

Startup & Config Crashes

SymptomCauseFix
Container crash-loops, logs could not resolve external IPuse_external_ip: true without explicit node_ip in DockerSet 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/keyAdd domain: under turn:, provide cert_file/key_file, or remove tls_port for UDP-only TURN
field tls not found on startupLiveKit v1.12+ removed top-level tls: config fieldRemove tls: block from LiveKit YAML; use http:// / ws:// in Bedrud config
LIVEKIT_CONFIG env var not picked upEntrypoint 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: -cImage entrypoint is /livekit-server directly — command gets appended, not wrapped by shellDon’t use a shell wrapper, LIVEKIT_CONFIG env var is supported natively by the binary
Docker Compose: $LIVEKIT_CONFIG expands to empty stringCompose substitutes $VAR from host environment, not containerUse $$LIVEKIT_CONFIG to escape docker-compose variable substitution
YAML parsing error from LIVEKIT_CONFIGIncorrect YAML indentation or syntaxValidate: docker run --rm -e LIVEKIT_CONFIG livekit-server --config-body "$LIVEKIT_CONFIG"

Connectivity

SymptomCauseFix
Admin dashboard shows “LiveKit disconnected”Bedrud can’t reach LiveKit HTTP APIVerify internalHost in config; run curl http://<internalHost>/ from Bedrud host; check firewall
Token generated but client connection times outLiveKit WebSocket unreachable from browserCheck host in livekit: config (must be reachable by clients); verify DNS/firewall; test with wscat
Embedded LiveKit not startingMissing binary or permissionEnsure internal/livekit/bin/livekit-server exists (even empty file for build); check Bedrud server logs
Port 7880 already in useAnother process on same portChange livekit.port or use different Docker port mapping
Redis connection fails in LiveKit logsRedis unreachable or wrong addressVerify redis.address: in LiveKit YAML; check container network connectivity
curl http://127.0.0.1:7880 returns connection refusedLiveKit crashed during startupCheck docker logs / journalctl; look for RTC/TURN validation errors near the bottom of the log
Token expired before client connectedShort JWT validity windowRequest a fresh token via POST /api/room/join before each connection attempt

Media & TURN

SymptomCauseFix
Participants join but no audio/videoUDP port range blocked or wrong node_ipOpen UDP 50000-60000; verify node_ip is the externally reachable address
Clients behind NAT can’t connectTURN not configured or ports blockedEnable 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 failureSet explicit node_ip and use_external_ip: false
Self-signed cert errors with external LiveKitBedrud’s skipTLSVerify is falseSet skipTLSVerify: true in Bedrud’s livekit: config
Clients connect via relay unnecessarilynode_ip is a private IP behind NATSet node_ip to the public IP or use use_external_ip: true with STUN access
TURN relay not used by clientsDirect WebRTC path is working (expected)Check chrome://webrtc-internalssrflx candidates = direct path, no TURN needed

Webhook & State

SymptomCauseFix
Database shows stale active participants after disconnectWebhook not configured for external LiveKitAdd webhook: block to LiveKit YAML with URL https://<domain>/api/livekit/webhook
Participants never marked inactiveFirewall blocking webhook delivery from LiveKit to BedrudCheck LiveKit logs for webhook delivery errors; ensure port 443/8090 is reachable from LiveKit
Room not cleaned up after all leaveempty_timeout / departure_timeout too highReduce 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, StartRoomCompositeEgress returns 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 the FileURL is 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

SymptomCauseFix
twirp error unauthenticated: permissions denied when starting recordingEgress JWT missing Room field in grantUpdate egressAuthContext to include Room: roomName in the VideoGrant
twirp error unauthenticated: permissions denied even with valid API keyRedis not configured for LiveKit, or egress workers unavailableAdd 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 dirAdd egress.s3: block to LiveKit YAML pointing to S3-compatible storage
process_recording job fails to downloadExternal LK’s file URL points to inaccessible local pathConfigure 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.

関連情報