Integrating Discourse Voice with LiveKit

This guide walks through connecting Discourse’s Voice plugin to a LiveKit media server. By default, voice calls run peer-to-peer: every participant sends their audio directly to every other participant. That works great for small rooms, but bandwidth grows with room size. Routing calls through LiveKit keeps each participant’s upload constant no matter how many people join.

Prerequisites

Before you start, make sure you have:

  • A Discourse site running the Voice plugin enabled
  • A LiveKit server you can reach, with its API credentials. You can self-host LiveKit or use a managed offering such as LiveKit Cloud.
  • Admin access to your Discourse site.

Step 1 — Enable the Voice plugin

Voice ships bundled with Discourse. Go to Admin → Settings → Plugins and
turn on the Voice (voice_enabled) setting.

Step 2 — Provision a LiveKit server and note the credentials

You need three things from your LiveKit deployment: the WebSocket URL, the
API key, and the API secret.

These steps are relatively straightforward with LiveKit Cloud:

  1. Create a project in the LiveKit Cloud dashboard.
  2. From the project’s Settings → Keys page, reveal the API key and
    API secret.
  3. Note the WebSocket URL (for example wss://my-project.livekit.cloud).

For Self-hosted LiveKit installations, please refer to the respective documentation.

Step 3 — Configure the LiveKit connection in Discourse

Go to Admin → Plugins → Voice, open the settings and fill in the LiveKit section:

Setting What to enter
voice_livekit_url The WebSocket URL, e.g. wss://livekit.example.com (or ws:// for plain-HTTP labs).
voice_livekit_api_key The API key from Step 2.
voice_livekit_api_secret The API secret from Step 2.
voice_livekit_room_policy Which rooms use LiveKit.

The voice_livekit_room_policy setting decides how rooms pick a transport:

Policy Behaviour
disabled All calls run peer-to-peer (the default).
per_room Room creators/managers opt rooms in individually with a Use media server (SFU) checkbox in the room form.
all_rooms Every room is routed through LiveKit.

Step 5 — Optional extra settings

These settings fine-tune the integration:

Setting Purpose
voice_livekit_room_prefix Namespace prefix for room names on LiveKit. With multiple sites sharing one LiveKit server, set a unique prefix per site. Empty defaults to the site’s database name.
voice_livekit_mesh_fallback When a LiveKit token cannot be issued and the room is empty, start on the peer-to-peer mesh instead of failing the join. Off by default — silent degradation can hide a LiveKit outage.
voice_livekit_recording_enabled Allow room moderators to record calls that run on LiveKit. Recordings are produced by LiveKit Egress and stored on your LiveKit deployment’s storage (S3, GCS, local disk) — not uploaded to Discourse.
voice_livekit_recording_filepath The filepath passed to LiveKit Egress, e.g. voice/{room_name}-{utc}. Supports placeholders such as {room_name}, {room_id}, {time}, {utc}; the file extension is added automatically.

Step 6 — Optional: enable LiveKit webhooks

Webhooks are a reconcile-only backstop. When a participant’s connection dies
abruptly or a room ends, webhooks let the plugin clean up presence and room
state within seconds instead of waiting out the heartbeat TTL. They are
optional — if they can’t be delivered, calls still work, cleanup just takes
a little longer.

On the LiveKit server, add the webhook endpoint (the plugin shows the exact
snippet on its admin dashboard until the first webhook arrives):

# livekit.yaml
webhook:
  api_key: <your-api-key>
  urls:
    - https://forum.example.com/voice/livekit/webhook

Restart LiveKit. Deliveries are authenticated with the API secret —
no additional shared secret to configure.

Step 7 — Verify the integration

Check the Discourse Voice admin dashboard

Go to Admin → Plugins → Voice → Dashboard. Once any LiveKit setting is
present, the dashboard shows a LiveKit media server status card with
live checks:

  • configuration status (which settings are present and the active policy),
  • whether access tokens can be signed with the key pair,
  • whether the server is reachable and how many rooms are active,
  • the last automatic connectivity check,
  • whether webhooks are being received.

Use Refresh to run an on-demand probe.

Here is a screenshot of the Voice plugin dashboard with LiveKit enabled:

Check the LiveKit dashboard

There are details about the calls on the LiveKit dashboard, too. Here is an example:

3 likes