Files
OwnCord/docs/livekit-setup.md
T
J3vb 8a5b001e41 docs: add Docker + LiveKit deployment guide
- deployment.md: new Docker section with quick-start, config.yaml notes,
  data persistence, upgrade, and LiveKit reference
- livekit-setup.md: new Docker section with .env / livekit.yaml setup,
  node_ip explanation, and firewall table; companion process section
  retitled for clarity
2026-04-03 14:36:10 +02:00

7.6 KiB

LiveKit Setup Guide

LiveKit is an open-source SFU (Selective Forwarding Unit) that handles real-time voice and video. OwnCord uses it instead of rolling its own WebRTC stack -- LiveKit handles all the hard parts (DTLS, ICE, codec negotiation, simulcast) while OwnCord manages permissions, state, and room lifecycle.

There are two ways to run LiveKit alongside OwnCord:

Method Best for LiveKit managed by
Docker Compose Linux servers Docker (separate container)
Companion process Windows / bare-metal Linux OwnCord (auto-start)

Docker

When running OwnCord via docker compose, LiveKit runs as a separate container on the same internal network. OwnCord reaches it at ws://livekit:7880 via Docker's internal DNS — no port forwarding needed between containers.

Setup

  1. Edit .env (in Server/) — set LIVEKIT_API_KEY and LIVEKIT_API_SECRET:

    LIVEKIT_API_KEY=my-unique-key
    LIVEKIT_API_SECRET=my-secret-at-least-32-characters-long
    
  2. Edit livekit.yaml (copy from livekit.yaml.example) — use the same key/secret and set your public IP:

    port: 7880
    rtc:
      tcp_port: 7881
      port_range_start: 50000
      port_range_end: 60000
      node_ip: "YOUR_SERVER_PUBLIC_IP"   # required for remote clients
    keys:
      my-unique-key: my-secret-at-least-32-characters-long
    logging:
      level: info
    
  3. Leave voice.livekit_binary unset in your config.yaml. The voice.livekit_url should be ws://livekit:7880 (Docker DNS).

  4. Open firewall ports on your host:

    Port Protocol Purpose
    7880 TCP LiveKit signaling
    7881 TCP TCP fallback for WebRTC
    50000-60000 UDP WebRTC media

node_ip is required for remote clients. Without it, LiveKit advertises internal Docker IP addresses as ICE candidates, which are unreachable from the internet. If your cloud VM has a metadata service (AWS, GCP, DigitalOcean) you can use use_external_ip: true instead.


Companion Process (Windows / bare-metal Linux)

1. Get the LiveKit Binary

Download livekit-server for your platform from one of:

Extract the binary somewhere permanent (e.g. C:\livekit\livekit-server.exe).


2. Server Configuration

LiveKit settings live in the voice: section of config.yaml:

voice:
  livekit_api_key: "devkey"
  livekit_api_secret: "owncord-dev-secret-key-min-32chars"
  livekit_url: "ws://localhost:7880"
  livekit_binary: "C:/livekit/livekit-server.exe"
  quality: "medium"
Field Purpose Default
livekit_api_key Shared API key between OwnCord and LiveKit "devkey"
livekit_api_secret Shared secret for JWT signing (min 32 chars) "owncord-dev-secret-key-min-32chars"
livekit_url LiveKit WebSocket URL ws://localhost:7880
livekit_binary Path to livekit-server binary. Empty = assume externally managed "" (disabled)
quality Default voice quality preset "medium"

Environment variable overrides use the OWNCORD_ prefix: OWNCORD_VOICE_LIVEKIT_API_KEY, OWNCORD_VOICE_LIVEKIT_API_SECRET, etc.

Warning

: The server logs a warning at startup if you use the default dev key/secret. Always change these for production.


3. Ports and Firewall

Port Protocol Purpose
7880 TCP (HTTP/WS) LiveKit signaling (WebSocket + REST API)
7881 TCP LiveKit internal RTC (TURN/TCP fallback)
50000-60000 UDP Media transport (RTP audio/video)

For LAN-only setups, ensure these ports are open on Windows Firewall. For remote access, forward these through your router or use Tailscale.


4. How the Companion Process Works

When livekit_binary is set, OwnCord manages LiveKit as a companion process:

  1. Config generation: OwnCord auto-generates data/livekit.yaml with the API key/secret, port 7880, and UDP range 50000-60000
  2. Process launch: livekit-server --config data/livekit.yaml
  3. Crash recovery: Exponential backoff restart (3s -> 6s -> 12s ... up to 60s), gives up after 10 consecutive rapid failures
  4. Health checks: GET http://localhost:7880/ verifies LiveKit is responding
  5. Graceful shutdown: Stops the process when OwnCord shuts down (5s timeout before kill)

If livekit_binary is empty, OwnCord assumes LiveKit is managed externally (e.g. Docker, systemd, or manual start).


5. Token Flow

How a client joins voice:

Client                     OwnCord Server              LiveKit Server
  |                             |                           |
  |-- voice_join (channel_id)-->|                           |
  |                             |-- check CONNECT_VOICE     |
  |                             |-- persist to voice_states |
  |                             |-- GenerateToken()         |
  |<-- voice_token ------------|                           |
  |    { token, url,           |                           |
  |      direct_url }          |                           |
  |                             |                           |
  |-- connect with JWT --------|-------------------------->|
  |<--- media streams ----------|--------------------------|

Token details:

  • Room name: "channel-{channelID}"
  • Identity: "user-{userID}"
  • TTL: 24 hours (refresh at 23h)
  • canPublish is derived from the SPEAK_VOICE permission
  • canSubscribe is always true
  • Client can request refresh via voice_token_refresh (rate limited to 1/60s)

Client connection paths:

  • Proxy path (/livekit): Client connects through OwnCord's HTTPS server. Avoids mixed-content issues.
  • Direct URL (ws://localhost:7880): Used when the client is on localhost.

6. Webhook Integration

LiveKit sends webhooks to POST /api/v1/livekit/webhook. The endpoint verifies the JWT and handles participant_left to clean up ghost voice states when a user disconnects from LiveKit without sending a voice_leave message.


7. Troubleshooting

Symptom Cause Fix
"voice not configured" error LiveKit client failed to initialize Check livekit_api_key and livekit_api_secret are set and secret is >= 32 chars
"failed to generate voice token" API key/secret mismatch Ensure config.yaml key/secret match what LiveKit is using
Voice connects but no audio Firewall blocking UDP 50000-60000 Open UDP port range in Windows Firewall
"backend unavailable" from /livekit proxy LiveKit not running on port 7880 Check livekit_binary path or start LiveKit manually
"too many rapid failures, giving up" in logs LiveKit binary crashes on startup Run livekit-server --config data/livekit.yaml manually to see errors
Mixed content / insecure WS error Client using direct URL over HTTPS page Client should use the /livekit proxy path
GET /api/v1/livekit/health returns degraded LiveKit server not reachable Verify LiveKit is running: curl http://localhost:7880

8. Production Checklist

  • Change livekit_api_key from "devkey" to a random string
  • Change livekit_api_secret to a random 32+ character string
  • Open firewall ports: 7880/TCP, 50000-60000/UDP
  • If using ACME/manual TLS, ensure LiveKit proxy at /livekit is working
  • Test voice by joining a voice channel from two clients
  • Check /api/v1/livekit/health returns {"status": "ok"}