Video Conferencing System Design Template (WebRTC SFU)
WebRTC video meetings: mesh vs MCU vs SFU, simulcast layers, bandwidth estimation and layer selection, SFU cascading, raw track recording, SFrame E2EE
How to use: Use it when designing or reviewing a multiparty audio and video meeting service, or preparing the video conferencing interview question. Fill the bitrate ladder and receive mix in section 3 first, because SFU egress, not CPU, sizes the fleet. The main pitfall is drawing media and signaling on the same arrows and forgetting that layer switches need key frames.
Preview
System Design: [Video conferencing service name]
Reference design for a multiparty video conferencing service built on WebRTC: browser and native clients, a signaling service, selective forwarding units (SFUs) with simulcast, bandwidth estimation and layer selection, cascading across regions for large meetings, recording and optional end-to-end encryption. It builds on the W3C Recommendation "WebRTC: Real-Time Communication in Browsers" (13 March 2025), RTP (RFC 3550), RTP topologies (RFC 7667), simulcast (RFC 8853), congestion control feedback (RFC 8888) and SFrame (RFC 9605). It follows the structure of the System Design Document template, adds a 45-minute interview path, access-pattern and failure-mode tables and a cited "How real systems do it" box, and is pre-filled with a worked design to adapt. Replace every
[bracketed]value and every row marked Example; values marked Example are assumptions, not benchmarks.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-VC-001] |
| System / feature | [Real-time meetings: audio, video, screen share] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Owner | [@owner] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal] |
| Difficulty | Advanced |
| WebRTC baseline | [W3C Recommendation, 13 March 2025] |
| Related | [Live streaming design], [Notification service], [ADR: SFU versus MCU] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| Media / real-time engineering | [@name] | [Decision] | YYYY-MM-DD |
| SRE / network operations | [@name] | [Decision] | YYYY-MM-DD |
| Security and privacy | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Replaced live composite recording with raw track recording after cost review |
| 0.3 | YYYY-MM-DD | [@name] | Example: Added SFU cascading for meetings that span regions |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: The current product runs small calls as a peer-to-peer mesh. Each participant uploads one copy of its video to every other participant, so a six-person call needs about 7.7 Mbps of upload per person (section 3.2), and calls above four people freeze on home connections. Customers also ask for recording, meetings of several hundred people and an end-to-end encrypted mode for sensitive calls.]
1.2 Proposed solution
[Example: Clients join through a signaling service over a WebSocket and send media to the nearest SFU over WebRTC (ICE, DTLS-SRTP). Each sender publishes three simulcast layers of its camera. The SFU forwards packets without decoding them and picks, for every receiver, the layer that fits the receiver's estimated bandwidth and screen layout. Meetings whose participants are in several regions use one SFU per region, linked by a cascade that carries each layer once between regions. Recording writes the raw forwarded tracks to object storage and composes a playable file after the meeting. An optional end-to-end encrypted mode wraps every frame with SFrame keys that the server never sees.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Mesh, MCU or SFU? | SFU for every meeting, including two-person calls, so one code path serves all sizes | 7.1, 10 |
| Q-02 | Example: How many simulcast layers? | Three layers (180p, 360p, 720p) for camera, one high layer for screen share | 7.2 |
| Q-03 | Example: Live composite recording or raw track recording? | Raw tracks during the meeting, composite afterwards | 7.5 |
| Q-04 | Example: Is end-to-end encryption on by default? | Opt-in per meeting; it disables server recording and transcription | 7.6 |
| [Q-nn] | [Question] | [Option] | [Section] |
1.4 45-minute interview path
Use this order when the document serves an interview or a short design review. The written sections below are the long form of each step.
| Minutes | Cover | Output on the whiteboard |
|---|---|---|
| 0 to 5 | Scope: meeting size, audio and video, screen share, recording; below-the-line items (2.3) | Three to five core requirements, latency target |
| 5 to 10 | Estimates: upload per sender, download per receiver, SFU egress, server count (section 3) | "Egress, not CPU, sizes the fleet" |
| 10 to 15 | Interfaces: join API, signaling messages, what travels over RTP (section 4) | Join call plus offer, answer and candidate messages |
| 15 to 25 | High-level design: clients, signaling, SFU, TURN, recording (section 6) | One diagram with media and signaling paths drawn apart |
| 25 to 40 | One deep dive: simulcast and layer selection with bandwidth estimation (default, 7.2 and 7.3), or cascading for large meetings (7.4), or recording (7.5) | Options, choice, reason |
| 40 to 45 | Failures (SFU crash, packet loss, region loss) and the 10x row in section 10 | Two failure rows |
Start the default deep dive with the naive answer, a mesh where every peer sends to every other peer, and show that upload grows with meeting size. Then introduce the MCU, which fixes upload but decodes and re-encodes every stream, and finally the SFU, which only forwards packets but needs the sender to offer several qualities so that each receiver can get one that fits.
2. Requirements
2.1 Core functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: Create a meeting, share a link, join from a browser or native app | Must |
| FR-02 | Example: Two-way audio and video for up to 100 interactive participants | Must |
| FR-03 | Example: Screen sharing by one presenter at a time | Must |
| FR-04 | Example: Active speaker view and gallery view with up to 25 tiles | Must |
| FR-05 | Example: Host controls: mute others, remove participant, lock meeting, waiting room | Must |
| FR-06 | Example: Cloud recording available within 30 minutes after the meeting ends | Should |
| FR-07 | Example: Optional end-to-end encrypted meetings | Should |
| FR-08 | Example: View-only attendees up to 1 000 per meeting | Could |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | Media latency | Example: Glass-to-glass p95 at or below 250 ms within one region, 400 ms across regions | Synthetic clients with timestamped frames |
| NFR-02 | Join time | Example: p95 from click to first audio at or below 3 s | Client telemetry |
| NFR-03 | Availability | Example: 99.95% of meeting-minutes without a media interruption longer than 5 s | Client reconnect events |
| NFR-04 | Quality under loss | Example: Audio remains intelligible at 10% random packet loss | Network emulation test |
| NFR-05 | Scale | Example: 2 million concurrent participants at peak | Load test with synthetic publishers |
| NFR-06 | Privacy | Example: Media encrypted in transit on every hop; E2EE mode keeps media keys off servers | Security review, packet capture |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Below the line (out of scope)
Name these explicitly so reviewers and interviewers do not pull the design toward them.
- [Example: broadcast to unlimited viewers through a CDN (see the live streaming design)]
- [Example: telephone dial-in through a PSTN gateway]
- [Example: live captions and translation (separate speech pipeline consuming audio tracks)]
- [Example: calendar integration and scheduling user interface]
- [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
| Input | Symbol | Value | Source |
|---|---|---|---|
| Peak concurrent participants | Example: 2 000 000 | Usage forecast | |
| Average interactive meeting size | Example: 6 | Product analytics | |
| Simulcast camera layers (180p, 360p, 720p) | Example: 0.15, 0.5, 1.5 Mbps | Encoder settings | |
| Audio bitrate incl. packet overhead | Example: 0.04 Mbps | Opus at about 32 kbps plus headers | |
| Share of participants with camera on | Example: 0.7 | Product analytics | |
| Receive mix per participant | Example: 1 large tile at 720p, 3 small tiles at 180p, 3 loudest audio streams | Default layout | |
| Usable egress per SFU server | Example: 6 Gbps (10 Gbps NIC at 60%) | Load test | |
| Average meeting length | Example: 45 min | Product analytics |
3.2 Why not a mesh
In a mesh each sender uploads its high layer and audio to every other participant:
Many home and mobile uplinks cannot sustain this, so a server in the media path is required for meetings above three or four people.
3.3 Upload and download per participant with an SFU
The sender uploads three layers once; the receive mix stays constant as the meeting grows, because the layout caps the number of tiles.
3.4 SFU fleet
Ingress alone would need 515 servers, so egress is the binding constraint. With 25% headroom for failover and uneven placement the fleet is about 870 SFU servers across all regions. With an average packet of about 1 100 bytes, egress is about 470 million packets per second, about 680 000 per server before headroom, so the forwarding path must avoid per-packet allocation and system calls where possible (batching sends, kernel bypass or large socket buffers).
3.5 Signaling and recording
- Joins: joins/s on average at peak, about 7 400 joins/s in the minute after the hour when meetings start together. At about 10 signaling messages per join that is about 74 000 messages/s, small for a horizontally scaled WebSocket tier.
- Recording: if 5% of 4 million daily meetings are recorded, live composite recording at 2.064 Mbps (720p plus 64 kbps audio) writes about 139 TB per day (12.5 PB for 90 days) and needs about 16 700 compositors running at peak, about 33 000 vCPU at 2 vCPU each. Raw track recording of the 360p layers ( Mbps per meeting) writes about 158 TB per day (14.2 PB for 90 days) but needs no live decoding.
3.6 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Mesh upload at 6 people | 7.7 Mbps per sender | Server in the media path from day one |
| Upload per participant | 1.545 Mbps | Simulcast with three layers is affordable for senders |
| SFU egress | 4.14 Tbps | Fleet sized by NIC egress, about 870 servers with headroom |
| Packets | About 680 000 per server per second | Forwarding path optimised for packet rate |
| Joins | About 7 400/s at the top of the hour | Stateless signaling tier, meeting state in a shared store |
| Recording | 139 to 158 TB per day | Raw tracks now, composite later; lifecycle rules on storage |
4. API design
4.1 Interfaces
| Interface | Operation | Purpose |
|---|---|---|
| REST | POST /v1/meetings | Create a meeting; returns ID, join link, settings |
| REST | POST /v1/meetings/{id}/join-tokens | Issue a short-lived signed join token for one participant and role |
| WebSocket | wss://signal.example.com/v1?token= | Signaling channel for one participant session |
| WebRTC | ICE, DTLS-SRTP, RTP and RTCP to the assigned SFU | Media and media feedback |
| REST | GET /v1/meetings/{id}/recordings | List recordings and their processing state |
| Webhook | meeting.ended, recording.ready | Notify integrations |
4.2 Join flow
The join token is a signed, short-lived credential (for example a JWT with a 60 s expiry) that names the meeting, the participant and the role. The signaling service validates it, picks an SFU (7.4) and returns ICE servers, including TURN credentials that expire with the session.
{"type": "joined", "meetingId": "mtg_7Qx2", "participantId": "p_19", "role": "host", "sfu": {"region": "eu-west", "node": "sfu-eu-w-114"}, "iceServers": [{"urls": ["turns:turn.eu-west.example.com:443?transport=tcp"], "username": "1730300000:p_19", "credential": "[short-lived]"}]}
4.3 Signaling messages
| Message | Direction | Content |
|---|---|---|
offer / answer | Both | SDP per JSEP (RFC 9429), including the simulcast rid list |
candidate | Both | Trickled ICE candidates (RFC 8445) |
layout | Client to server | Which participants the client shows and at what size |
track-published / track-removed | Server to client | Track list changes for the meeting |
active-speaker | Server to client | Current dominant speaker from audio levels |
host-action | Both | Mute, remove, lock, admit from waiting room |
e2ee-key-epoch | Server to client | Epoch announcement only; key material never passes through the server in E2EE mode |
The layout message matters for capacity: a client that shows only the
active speaker should not receive 24 thumbnails it will never draw.
5. Data model
5.1 Entities
Meetings and recordings are durable records; sessions, tracks and SFU assignments are live state that exists only while a meeting runs.
5.2 Storage choices
| Data | Store | Key | Why |
|---|---|---|---|
| Meetings, recordings metadata | Example: Relational database, replicated | meeting_id | Low write rate, needs transactions for host settings |
| Live meeting state (sessions, tracks, SFU assignment) | Example: In-memory store with replication, per region | meeting:{meeting_id} | Read on every signaling event; lost state can be rebuilt from clients |
| SFU load registry | Example: Service discovery with TTL keys | sfu:{region}:{node_id} | Heartbeats every 2 s, expire on node loss |
| Recorded tracks | Object storage | rec/{meeting_id}/{track_id}/{segment} | Large sequential writes, lifecycle rules |
| Quality telemetry | Event pipeline to analytics store | Time, meeting | High volume, never on the media path |
5.3 Access patterns
| Access pattern | Frequency | Key or index that serves it | Partition key rationale |
|---|---|---|---|
| Find the SFU that hosts a meeting in this region | Every join (about 7 400/s at peak) | meeting:{meeting_id} hash, field per region | One meeting is the unit of placement, so all its state lives under one key |
| Pick the least-loaded SFU for a new meeting | Every first join in a region | Sorted set of nodes by egress load per region | Per-region set; joins never scan other regions |
| Broadcast a track change to all participants | Every publish or unpublish | Meeting channel on the signaling tier keyed by meeting_id | Signaling nodes subscribe to the meetings they serve |
| List recordings for a meeting | Rare | meeting_id index in the relational store | Joins with meeting ownership for access checks |
| Compose a recording after the meeting | Once per recording | Object prefix rec/{meeting_id}/ | One prefix per meeting lets a worker list every track in one call |
| Analyse call quality by network and client | Batch | Telemetry partitioned by day and region | Never touches live state |
6. High-level design
Media and signaling take different paths. Media goes from client to SFU over UDP when possible, through TURN when a firewall blocks UDP.
| Component | Responsibility | Scales by |
|---|---|---|
| Meeting API | Meetings, join tokens, recordings, webhooks | Stateless instances |
| Signaling service | WebSocket sessions, SDP exchange, layout, host actions, SFU assignment | Connections per node; stateless apart from sockets |
| SFU | Terminate DTLS-SRTP, forward RTP, select layers, generate RTCP feedback | Egress bandwidth and packet rate (section 3.4) |
| TURN relay | Relay media for clients that cannot reach the SFU directly | Relayed bandwidth (about 10% of sessions, Example) |
| Recorder sink | Write forwarded packets of recorded meetings into segments | Recorded meetings |
| Composer jobs | Decode raw tracks and produce a playable file after the meeting | Batch workers on spare capacity |
| SFU registry | Load and health of each SFU, heartbeats | Small, per region |
7. Detailed design
The deep dives in this section, each with options, choice and reason:
| Deep dive | Options considered | Choice | Reason | Section |
|---|---|---|---|---|
| Topology | Mesh, MCU, SFU | SFU | Constant upload for senders, no server transcoding | 7.1 |
| Quality adaptation | Single stream with server transcoding, simulcast, scalable video coding (SVC) | Simulcast, SVC where every client supports it | Works with every WebRTC client today | 7.2, 7.3 |
| Large and multi-region meetings | One SFU per meeting, cascade of SFUs | Cascade by region | Each participant stays close to its SFU | 7.4 |
| Recording | Live composite bot, raw track capture | Raw tracks, composite after the meeting | No live decoding, cheaper at peak | 7.5 |
7.1 Topology: mesh, MCU or SFU
RFC 7667 describes the RTP topologies; the three relevant here are:
| Topology | What the server does | Sender upload | Server cost | Fits |
|---|---|---|---|---|
| Mesh (point to point between every pair) | Nothing in the media path | Grows with meeting size | None | Two or three people |
| MCU (mixer) | Decodes every stream, mixes one composite per layout, re-encodes | One stream | High CPU per meeting, added latency of a decode and encode | Legacy room systems, very weak receivers |
| SFU (selective forwarding middlebox) | Forwards packets and selects which stream each receiver gets | One stream per layer | Bandwidth and packet rate, little CPU | Interactive meetings of any size |
The SFU wins because it adds almost no latency (no decode), keeps CPU cost low and lets every receiver choose its own layout. Its weakness, that the sender must offer several qualities, is solved by simulcast.
7.2 Simulcast
Each sender encodes its camera at several resolutions at once and sends them
as separate RTP streams, identified by a restriction identifier (rid)
negotiated in SDP as RFC 8853 describes. In the W3C WebRTC API the sender
sets sendEncodings on addTransceiver, one entry per layer, with
rid and scaleResolutionDownBy.
pc.addTransceiver(cameraTrack, {
direction: "sendonly",
sendEncodings: [
{ rid: "l", scaleResolutionDownBy: 4, maxBitrate: 150000 },
{ rid: "m", scaleResolutionDownBy: 2, maxBitrate: 500000 },
{ rid: "h", scaleResolutionDownBy: 1, maxBitrate: 1500000 }
]
});
The SFU tells a sender to pause a layer nobody is receiving (for example the 720p layer when nobody has that participant in the large tile), which saves the sender's uplink and CPU. Screen share uses one high-resolution, low frame rate stream instead, because text must stay sharp.
7.3 Bandwidth estimation and layer selection
Two estimates drive adaptation:
- Sender side: the sender's congestion controller estimates how much it can upload and sets the encoder bitrates. Browsers use delay-based and loss-based control (the Google Congestion Control approach, described in the expired draft-ietf-rmcat-gcc-02) driven by transport-wide feedback. RFC 8888 standardises an RTCP feedback format for congestion control.
- Receiver side at the SFU: the SFU sends packets to each receiver and estimates that receiver's downlink from feedback about arrival times and loss. It then picks layers.
The layer selection loop runs per receiver every few hundred milliseconds:
- Start from the receiver's layout: one large tile, up to small tiles, the three loudest audio streams.
- Audio first: audio is never dropped for video.
- Give each small tile its lowest layer.
- Give the large tile the highest layer that still fits the estimate.
- If even the lowest layers do not fit, pause the small tiles and keep the large tile at its lowest layer; tell the client so it can show avatars.
Worked example with the ladder from 3.1 (Example):
| Receiver estimate | Large tile | Small tiles | Audio | Total | Result |
|---|---|---|---|---|---|
| 2.5 Mbps | 720p (1.5) | 3 x 180p (0.45) | 3 x 0.04 (0.12) | 2.07 Mbps | Full layout |
| 1.2 Mbps | 360p (0.5) | 3 x 180p (0.45) | 0.12 | 1.07 Mbps | Large tile steps down |
| 0.5 Mbps | 180p (0.15) | paused | 0.12 | 0.27 Mbps | Small tiles show avatars |
A layer switch must happen on a key frame of the new layer, otherwise the receiver cannot decode. The SFU requests a key frame from the sender (RTCP picture loss indication) and switches when it arrives, and it rewrites sequence numbers and timestamps so the receiver sees one continuous stream. To avoid oscillation, step up only after the estimate has stayed above the next layer's bitrate plus 15% for a few seconds; step down at once.
Packet loss is handled per hop: the SFU retransmits lost packets to a receiver from its own short buffer (NACK), so a lossy receiver does not cause retransmissions from the sender to everyone. Audio relies on Opus forward error correction instead, because a retransmission arrives too late for speech.
7.4 Placement and cascading
- Placement: the first participant in a region creates the meeting's presence on the least-loaded SFU of that region (by egress, not session count). Later participants in the same region join the same node, so one meeting stays on one SFU per region while it fits.
- Cascade: when participants are in several regions, each region's SFU forwards to the other region's SFU only the layers that remote receivers need, once per layer and not once per receiver. A 40-person meeting split across two regions crosses the long-haul link with a few streams instead of dozens.
- Large meetings: a meeting above the capacity of one SFU spreads its receivers over several SFUs in the same region connected as a tree. View-only attendees beyond about 1 000 move to the live streaming path, which trades seconds of latency for CDN scale.
7.5 Recording
Two options were compared for FR-06:
| Option | How it works | Cost at peak | Quality | Chosen |
|---|---|---|---|---|
| Live composite bot | A headless client joins, renders the layout, encodes one file | About 33 000 vCPU (3.5) | What attendees saw | No |
| Raw track capture | The SFU copies forwarded packets of each track into segments; a composer decodes after the meeting | Storage writes only during the meeting, batch CPU afterwards | Can render several layouts later | Yes |
Raw capture stores the 360p layer of each camera, the screen share and audio. Composition runs on batch capacity within the 30-minute target in FR-06. Participants are told that recording is on (visual indicator and an audible notice) before any packet is captured.
7.6 Security and end-to-end encryption
- Transport: WebRTC requires DTLS-SRTP, so every media hop is encrypted. The SFU terminates SRTP, so in standard mode the operator can access media on the server (needed for recording and transcription).
- E2EE mode: clients encrypt each encoded frame with SFrame (RFC 9605) before packetisation. Keys come from a group key agreement between clients, for example MLS (RFC 9420); the server only relays opaque handshake messages and sees epoch numbers. The SFU still routes packets and selects layers because RTP headers stay readable. Server recording, transcription and dial-in are disabled in this mode, and the UI shows a verification code participants can compare.
- Access: join tokens are short-lived and bound to a role; a waiting room and meeting lock prevent uninvited joins; TURN credentials expire with the session.
7.7 How real systems do it
How real systems do it (paraphrased, not copied). Discord's engineering post "How Discord Handles Two and Half Million Concurrent Voice Users using WebRTC" (Jozsef Vass, 10 September 2018) explains that all calls are routed through Discord's own servers rather than peer to peer, because peer-to-peer cost rises with participants and routing through servers hides user IP addresses. A home-grown C++ SFU forwards audio and video and uses receiver reports to tell senders how much bandwidth they have. Native clients use a customised WebRTC media engine with a much smaller signaling exchange than full SDP, while browsers use the standard API. Signaling services assign a voice server from those registered in a service discovery system, and when a voice server fails its health check clients reconnect to a new one. The post reports more than 850 voice servers in 13 regions. Read the original in 12.3; this box keeps only the general idea.
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| SFU egress | Place by egress load, pause unused layers, layout-driven subscriptions | Example: 6 Gbps per server at 60% NIC |
| Packet rate | Batched sends, preallocated buffers, one forwarding thread per core | Example: About 1 million packets/s per server in load test |
| Signaling | Stateless WebSocket nodes, meeting state in a regional store | [Connections per node] |
| Join storms at the top of the hour | Pre-warmed SFU capacity, join token issuance spread across API nodes | Example: 7 400 joins/s |
| Large meetings | SFU tree inside a region, cascade between regions, view-only to streaming | Example: 1 000 interactive receivers |
Latency budget within one region (target p95 250 ms glass to glass):
| Step | Budget |
|---|---|
| Capture and encode | 40 ms |
| Sender to SFU network | 30 ms |
| SFU forwarding | 2 ms |
| SFU to receiver network | 30 ms |
| Jitter buffer | 60 ms |
| Decode and render | 30 ms |
| Headroom | 58 ms |
9. Reliability, security and operations
9.1 Failure modes
| Component | Failure | Detection | Blast radius | Mitigation | RTO |
|---|---|---|---|---|---|
| SFU node | Process crash or host loss | Missed heartbeats, client ICE failure | Meetings on that node in one region | Clients reconnect through signaling to a new node; signaling replays track list | Example: 5 s |
| SFU region | Region outage | Registry empty, synthetic calls fail | Participants in that region | Signaling assigns the next nearest region; higher latency accepted | Example: 30 s |
| Signaling node | Crash | WebSocket close | Sessions on that node lose control messages, media continues | Client reconnects with session ID and resumes | Example: 3 s |
| TURN relay | Overload or outage | Allocation failures | Clients behind restrictive firewalls | Multiple TURN pools, TLS on 443 fallback | Example: 60 s |
| Network | Burst loss on a receiver link | Receiver loss and delay metrics | One receiver | NACK from SFU buffer, layer step-down, Opus FEC | Immediate |
| Recorder sink | Object storage write errors | Segment upload failures | Recordings of affected meetings | Local spool for 15 minutes, then alert host that recording is incomplete | Example: 15 min |
| [Component] | [Failure] | [Signal] | [Scope] | [Mitigation] | [Time] |
9.2 Security and abuse
| Threat | Control |
|---|---|
| Uninvited joins ("meeting bombing") | Join tokens, waiting room, lock, host removal, rate limits on join attempts |
| Media interception | DTLS-SRTP on every hop; E2EE mode with SFrame for sensitive meetings |
| IP address exposure between participants | All media through SFU or TURN, never peer to peer |
| TURN abuse as an open relay | Short-lived credentials bound to a session, permissions only toward SFU addresses |
| Recording without notice | Server enforces visible and audible notice before capture starts |
| [Threat] | [Control] |
9.3 Observability
| Signal | Metric or log | Alert threshold |
|---|---|---|
| Media quality | Freeze rate, receiver loss, round-trip time per region | Freeze rate doubles vs. 7-day baseline |
| Join success | Joins with first audio within 3 s | Below 99% for 10 min |
| SFU load | Egress Gbps, packets/s, CPU per node | Egress above 70% of NIC |
| Reconnects | Client reconnects per 1 000 meeting-minutes | Doubling vs. baseline |
| TURN share | Sessions relayed through TURN | Above 20% (firewall or ICE regression) |
9.4 Cost estimate
| Item | Unit cost | Quantity | Monthly cost |
|---|---|---|---|
| SFU servers | [$/server-month] | Example: About 870 | [$] |
| Egress bandwidth | [$/GB] | Example: 4.14 Tbps at peak, measure 95th percentile | [$] |
| TURN relays | [$/server-month] | [Count] | [$] |
| Recording storage | [$/GB-month] | Example: About 14 PB at 90-day retention | [$] |
| Total | [$] |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Topology | Mesh; MCU; SFU | SFU for all sizes | Constant sender upload, no transcoding, per-receiver layouts | Server bandwidth bill; receivers decode several streams |
| Adaptation | Simulcast; SVC; server transcoding | Simulcast now, SVC where supported | Works with all WebRTC clients | Sender uploads about 43% more than its top layer alone |
| Two-person calls | Peer to peer; SFU | SFU | One code path, no IP exposure | Small added latency and server cost |
| Recording | Live composite; raw tracks | Raw tracks | No live decoding at peak | Recording ready minutes after the meeting, not instantly |
| E2EE | Always on; opt-in; none | Opt-in | Keeps recording and transcription for most meetings | Two modes to test and explain |
| What changes at 10x / 0.1x | 10x (20 million concurrent): about 41 Tbps egress, 6 900 servers at 10 Gbps or about 2 760 at 25 Gbps and 60%, so move to 25 or 100 Gbps NICs, push regions closer to users and negotiate peering. 0.1x (200 000 concurrent): about 69 SFU servers, one or two regions, and a managed TURN service | Review at each doubling of peak participants | Egress and peering cost grow first | [Note] |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Signaling, single-region SFU, audio and one video layer | 25-person internal meetings for 2 weeks | YYYY-MM-DD |
| M2 | Simulcast, layer selection, NACK and key frame handling | Freeze rate below target on emulated 1.2 Mbps links | YYYY-MM-DD |
| M3 | Multi-region cascade, TURN pools | Cross-region p95 latency at or below 400 ms | YYYY-MM-DD |
| M4 | Raw track recording and composer | Recordings ready within 30 min for 99% of meetings | YYYY-MM-DD |
| M5 | E2EE mode with SFrame | External security review closed | YYYY-MM-DD |
11.2 Rollout and rollback
[Example: New SFU releases go to 1% of new meetings in one region, then 10%, then all, with automatic rollback when the freeze rate or reconnect rate rises by more than 20% against the previous release. Meetings already running stay on their node until they end; nodes are drained, never killed.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: Browser changes break simulcast negotiation | Medium | High | Run browser beta channels in the synthetic call suite | [@name] |
| R-02 | Example: Egress cost exceeds plan | Medium | High | Layout-driven subscriptions; pause unused layers; regional peering | [@name] |
| R-03 | Example: E2EE key handling flaw | Low | High | Use a reviewed MLS library; external audit before launch | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
12. Appendices
12.1 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Which codecs do we require (VP8, VP9, AV1, H.264) for each client platform? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Do recordings need a separate legal notice per jurisdiction where participants join? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.2 Glossary
| Term | Meaning |
|---|---|
| SFU | Selective forwarding unit: a server that forwards RTP packets and chooses which streams each receiver gets, without decoding |
| MCU | Multipoint control unit: a server that decodes, mixes and re-encodes streams into one composite |
| Simulcast | Sending the same source at several qualities as separate streams |
| SVC | Scalable video coding: one stream with layers a server can drop |
| ICE, STUN, TURN | NAT traversal: candidate gathering and checks, address discovery, media relay |
| PLI, NACK | RTCP messages asking for a key frame and for retransmission of lost packets |
| SFrame | Frame-level encryption for end-to-end protected media (RFC 9605) |
| [Term] | [Meaning] |
12.3 References
| Reference | Use |
|---|---|
| W3C, "WebRTC: Real-Time Communication in Browsers", W3C Recommendation, 13 March 2025 | Browser API, sendEncodings, simulcast |
| RFC 3550 RTP: A Transport Protocol for Real-Time Applications (July 2003) | RTP and RTCP |
| RFC 7667 RTP Topologies (November 2015) | Mesh, mixer and selective forwarding middlebox, 7.1 |
| RFC 8853 Using Simulcast in SDP and RTP Sessions (January 2021) | 7.2 |
| RFC 8888 RTCP Feedback for Congestion Control (January 2021) | 7.3 |
| draft-ietf-rmcat-gcc-02 Google Congestion Control (Internet-Draft of July 2016, expired January 2017) | Background for 7.3 |
| RFC 9429 JSEP (April 2024), RFC 8445 ICE, RFC 8656 TURN | Signaling and NAT traversal |
| RFC 9605 SFrame (August 2024), RFC 9420 MLS (July 2023) | 7.6 |
| Vass, "How Discord Handles Two and Half Million Concurrent Voice Users using WebRTC", Discord blog, 10 September 2018 | 7.7 |
Live Streaming System Design (system-design/design-live-streaming) | View-only attendees beyond the interactive limit |
Chat System Design (system-design/design-chat-system) | In-meeting chat and presence |
Notification Service Design (system-design/design-notification-service) | Meeting reminders and recording-ready notices |
Back-of-Envelope Estimation and Capacity Model (system-design/back-of-envelope-estimation) | Method for section 3 |
Standard and licence note
Based on the W3C WebRTC Recommendation (13 March 2025) and the IETF RFCs cited in 12.3, which are public specifications. The Discord engineering post is paraphrased in 7.7 only; no text, figures or code were copied apart from the short reported counts attributed to it, and all other numbers here are illustrative examples. WebRTC specifications and browser support change; check the current W3C and IETF pages before implementing. Original text written for this collection. Template licence: MIT.
Related templates
Video Streaming System Design Template (Reference Design)
Video upload and ABR streaming: chunked transcoding, renditions ladder, CMAF HLS/DASH, CDN, DRM note, view counts, storage tiers
System DesignAd Click Aggregation System Design Template (Reference Design)
45-minute path, click dedupe, event-time windows, exactly-once counting, exact and Count-Min top-K, batch reconciliation for billing, failure modes, 10x row
System DesignZanzibar-Style Authorization Service System Design Template
Relationship-based authorization service: relation tuples, namespace config, Check/Expand/List/Watch APIs, consistency tokens, caching, sharding, failure modes