Large Meeting Performance Issue (~70 Participants with Cameras On)

Here’s a community post you can use:


Hi ,

We’re facing a performance issue in our self-hosted LiveKit deployment and would appreciate guidance from the community.

Scenario

When approximately 70 participants join a meeting with their cameras enabled, the meeting starts becoming unstable:

  • Video streams begin to freeze/hang.

  • Most participants see their connection quality indicator turn red (Poor).

  • Overall meeting experience degrades significantly.

  • Audio/video quality becomes inconsistent across participants.

Current Configuration

const roomOptions = React.useMemo((): RoomOptions => {
  let videoCodec: VideoCodec | undefined = props.options.codec ? props.options.codec : 'av1';

  if (e2eeEnabled && (videoCodec === 'av1' || videoCodec === 'vp9')) {
    videoCodec = undefined;
  }

  const videoCaptureDefaults: VideoCaptureOptions = {
    deviceId: props.userChoices.videoDeviceId ?? undefined,
    resolution: props.options.hq ? VideoPresets.h540 : VideoPresets.h216,
  };

  const publishDefaults: TrackPublishDefaults = {
    dtx: true,
    videoSimulcastLayers: props.options.hq
      ? [VideoPresets.h540, VideoPresets.h360]
      : [VideoPresets.h216, VideoPresets.h90],
    red: !e2eeEnabled,
    videoCodec,
    degradationPreference: 'maintain-framerate',
  };

  return {
    videoCaptureDefaults,
    publishDefaults,
    audioCaptureDefaults: {
      deviceId: props.userChoices.audioDeviceId ?? undefined,
      noiseSuppression: true,
    },
    adaptiveStream: true,
    dynacast: true,
    e2ee: keyProvider && worker && e2eeEnabled ? { keyProvider, worker } : undefined,
    singlePeerConnection: true,
  };
}, [props.userChoices, props.options.hq, props.options.codec]);

Infrastructure Details

  • Self-hosted LiveKit deployment

  • Around 70 participants

  • Most participants have cameras enabled

  • Adaptive Stream enabled

  • Dynacast enabled

  • Using AV1 codec by default

  • Single Peer Connection enabled

Questions

  1. Are there any recommended optimizations for meetings of this size?

  2. Would changing from AV1 to VP8/VP9 help in large meetings?

  3. Are there specific server-side configurations we should tune for 70+ video participants?

  4. Is there a recommended participant limit when everyone has their camera enabled?

  5. Are there any monitoring metrics (CPU, bandwidth, SFU load, packet loss, etc.) that we should specifically look at to identify the bottleneck?

  6. Has anyone successfully hosted 70–100 participants with cameras on using LiveKit, and what configuration worked best?

Any suggestions, best practices, or production experiences would be greatly appreciated.

@sahiti, At ~70 cameras the limit is client downstream, not the SFU: the SFU only forwards the layers each client subscribes to (SFU), so every client is decoding close to 69 streams and the red “poor” is downstream congestion there. The fixes are codec and subscription, not server tuning.

Drop AV1 to VP8. AV1 and VP9 auto-enable SVC, and under SVC dynacast can only pause whole streams, not individual layers (codecs), so your simulcast layers and dynacast are not actually trimming per-layer bandwidth. VP8 uses classic simulcast, so dynacast can drop the layers each subscriber does not need, which is the real bandwidth win in a large grid.

VP9 has the same SVC limit, so VP8 specifically. You already fall back to VP8 under E2EE, so this only bites when E2EE is off.

Cap subscribed videos per client. Adaptive stream only pauses tracks whose UI element is hidden or tiny (subscribing), so with all 70 tiles visible nothing pauses. Paginate the grid so each client renders only the visible tiles; the rest stay paused and stop consuming downstream.

const publishDefaults: TrackPublishDefaults = {
  dtx: true,
  red: !e2eeEnabled,
  videoCodec: 'vp8',   // classic simulcast, so dynacast can drop layers per subscriber
  videoSimulcastLayers: [VideoPresets.h216, VideoPresets.h90],
  degradationPreference: 'maintain-framerate',
};

There is no fixed participant cap: it scales with how many streams each client decodes, which is exactly why pagination is the real lever.