Scope the requirements
Split the problem in half before you draw anything. There's a control plane (signalling, rooms, auth) that behaves like a normal web service, and a media plane (RTP, jitter buffers, SFUs) that behaves like nothing else in a system design interview. Interviewers score you on drawing that line.
Functional — what it must do
- Create a meeting, join by link/PIN, up to 500 participants.
- Real-time audio + video, screen share, chat sidebar.
- Adaptive bitrate under network stress; automatic reconnection.
- Recording to durable storage; live transcription optional.
- Explicitly cut: virtual backgrounds, breakout-room limits, phone-in — offer them back.
Non-functional — the numbers that shape the design
- Glass-to-glass latency under 200 ms p95 within a region.
- 10M concurrent meetings, ~40M concurrent participants at peak.
- Handle 30% packet loss without a call drop.
- Regional media routing — clients never traverse an ocean for a same-city call.
- 99.99% availability on join; call quality degrades before it drops.
The two planes have completely different SLAs. I'll design the control plane for correctness and the media plane for tail latency — they scale independently and share almost no infrastructure.
Back-of-envelope estimates
The media plane numbers dwarf everything else. One SFU box carries ~500 participants; the whole product is capacity planning around that.
| Quantity | Estimate | How you got there |
|---|---|---|
| Concurrent participants | ~40M | 10M meetings × ~4 avg; peak business hours drive this. |
| Bandwidth in | ~40 Tbps | 40M × ~1 Mbps average video. Cameras off cuts this in half in practice. |
| SFU boxes | ~80,000 | 1 SFU handles ~500 streams (100 Gbps NIC ÷ 200 kbps outbound). Spread across ~50 regions. |
| Signalling QPS | ~50k/s | join/leave/renegotiate — small compared to media. Handled by a stateless tier. |
| Recording storage | ~50 PB/mo | 1% of meetings recorded; ~500 MB per hour at 1080p. |
| STUN/TURN traffic | ~5% of media | NAT traversal fallback for restricted networks; the rest goes P2P-to-SFU. |
The bandwidth number is the design driver. I'm not sharding by user or by meeting size — I'm sharding by region because 200 ms is the whole latency budget.
Sketch the API
Control plane is REST + WebSocket for signalling. Media plane uses WebRTC — SRTP over UDP straight to the SFU.
- Signalling and media use different transports and different servers. Signalling is TCP/WebSocket (correctness); media is UDP/SRTP (latency). Mixing them is the classic beginner mistake.
- The join response is the pivot: the client learns which SFU to talk to based on server-side geo lookup, not client picks.
Data model
Control plane has small, cold data. The media plane has zero durable state — every SFU is a stateless forwarder.
Postgres for meetings/participants — a global-ish transactional store keeps auth and join semantics simple. Redis holds the per-meeting session so the join path is one memory lookup. The SFU has no database; it holds state in RAM and dies with no data loss.
High-level design
Draw the two planes explicitly. Everything on the left is normal web scaling; everything on the right is UDP, jitter buffers and geographic routing.
- Client hits join → signalling checks auth, looks up an existing SFU for that meeting in Redis (or picks one by the joiner's geo), returns the SFU endpoint and ICE credentials.
- Client opens a WebRTC session directly to the SFU: SDP negotiation over the signalling socket, media flows over UDP/SRTP straight to the SFU.
- SFU receives one stream per publisher, forwards N-1 streams per subscriber. No transcoding, no mixing — that's the SFU model.
- Recording is a special subscriber: the SFU sends a copy of each stream to a recorder process that muxes to disk and uploads to S3.
- On network loss, the client's ICE restart runs; if the same SFU is still reachable, the session survives; if not, the room service migrates the participant to a nearby SFU.
Deep dives — where the interview is won
SFU beats mesh at 5, MCU beats nothing
Three architectures exist. Mesh: every participant sends to every other participant. Upload bandwidth is N-1 streams — great for 2-3, unusable at 5 on residential upload. MCU (mixer): one server decodes every stream and produces a single mixed output. CPU-brutal at scale, adds transcoding latency, and loses per-stream layout flexibility. SFU (forwarder): one server relays streams without decoding — client uploads once, gets N-1 down. Costs bandwidth on the server, but bandwidth is a commodity and CPU is not.
For Zoom-scale, SFU is the only realistic option; every serious modern video product uses it. The follow-up worth having ready: SFUs can still do smart things — simulcast (client sends 3 resolutions, SFU picks per subscriber) and SVC (one scalable stream, SFU drops layers) let one uplink serve heterogeneous downlinks. Say simulcast unprompted.
Geo routing that keeps latency inside the physics budget
200 ms glass-to-glass leaves ~120 ms for the network. A same-continent hop is ~40 ms; cross-Atlantic is ~100 ms already. The design has to guarantee that a meeting where everyone is in Frankfurt runs on a Frankfurt SFU — not a Virginia one because that's where the control plane lives.
The trick: on the first join, pick the SFU nearest the joiner. Every subsequent joiner is routed to the same SFU, unless they're catastrophically far from it (say, > 150 ms), in which case a second SFU joins the mesh as a cascade — SFU-to-SFU forwarding across a fast backbone. Anycast + latency probing does the picking; the session record in Redis remembers what was picked.
Losing packets gracefully — the media plane's job, not the app's
UDP drops packets. WebRTC assumes it and has three lines of defence: NACK/RTX (retransmit lost packets, useful up to ~50 ms round trips), FEC (send redundant parity so the receiver can reconstruct without a retransmit), and PLC (packet loss concealment on the decoder side, so a lost audio frame becomes a plausible-sounding sample). None of these live in your app code — they're in the WebRTC stack. Your job is to configure them and to not undo them.
Above 20% loss, no combination of these keeps the picture up. The correct product answer is to shed load: drop the camera before you drop the call, degrade the video resolution before you degrade the audio, keep chat working when nothing else does. The signalling plane sees receiver reports, the SFU sees them too, and the client picks a smaller simulcast layer or turns the camera off. Say all four moves out loud.
Follow-ups you should expect
Why not P2P for two-person calls?
How do you record 500 concurrent streams into one file?
What breaks first at 100k participants in one meeting?
How do you handle a user behind symmetric NAT?
Encryption end-to-end for the meeting?
Where candidates lose the room
- Drawing one big media server that mixes streams — you've re-invented the MCU and lost the SFU's cost model.
- Routing media through the control-plane load balancer — TCP-terminated media adds head-of-line blocking and multi-hop latency.
- Ignoring geography — a Virginia-only SFU tier serving a European meeting spends 100 ms crossing the Atlantic.
- Forgetting that recording is a subscriber, not a special path — every SFU capability (simulcast, cascade) should apply to the recorder for free.
- Trying to make WebRTC do retransmission over TCP because "UDP is lossy" — TCP head-of-line blocking makes real-time video worse, not better.