Insights
Engineering Insights Nov 2020

WebRTC in Production: The Parts the Tutorials Skip

WebRTC tutorials show you how to connect two browsers on localhost. Production deployments are different. Here is what you need to know before you ship.

The Tutorial Works Fine

The WebRTC tutorials are good. They walk you through creating a RTCPeerConnection, setting up a local signaling mechanism, exchanging SDP and ICE candidates and establishing a peer connection. You run it in two Chrome tabs on the same machine, video appears and it feels like a solved problem.

Then you try to connect two real users on real networks and discover how much the tutorial left out.

NAT Traversal Is Not a Solvable Problem - It's a Statistical One

WebRTC is peer-to-peer, which means the two clients need to establish a direct connection. The problem is that most endpoints are behind NAT routers - a mechanism that assigns private IP addresses to devices and maps them to a shared public IP. NAT was not designed for inbound connections and WebRTC needs inbound connections.

ICE (Interactive Connectivity Establishment) is the framework WebRTC uses to find a path between two peers. It tries candidate types in preference order:

  1. Host candidates - direct connection using private IPs. Works only if both peers are on the same local network.
  2. Server-reflexive candidates - connection using public IPs discovered via STUN. Works for many common NAT types.
  3. Relay candidates - traffic relayed through a TURN server. Works when everything else fails.

In controlled tests on typical home broadband, STUN succeeds often. In enterprise networks with stricter NAT, corporate firewalls and proxies, STUN fails far more often than expected.

The practical rule: if you care about connection reliability, you need a TURN server. STUN-only deployments leave a predictable percentage of enterprise users unable to connect.

TURN Is Not Cheap

TURN relay passes all media traffic - both directions - through the TURN server. For video calls at typical resolutions, this is 1-3 Mbps per session. The bandwidth cost is the primary TURN operational cost and it is real.

Sizing a TURN server involves estimating your concurrent relay session load and allocating bandwidth accordingly. COTURN, the open-source TURN server, is reliable and well-maintained. Running it on an undersized instance causes quality degradation at exactly the moments when users are already struggling - NAT traversal failed, the call is on a relay path and now the relay is under resource pressure.

If you're using a managed WebRTC service that includes TURN, understand their bandwidth pricing model before you scale.

Signaling Is Simple Until It Isn't

WebRTC needs a signaling channel to exchange session setup information before the peer connection is established. The tutorials usually use a WebSocket server for this. In production:

  • Signaling connections drop. Clients need to reconnect and resume gracefully.
  • Signaling messages can arrive out of order. SDP and ICE candidates can interleave in unexpected sequences and your client needs to handle them.
  • Mobile clients go to the background. iOS in particular suspends WebSocket connections for background apps. Your signaling design needs to handle the case where a mobile participant disappears mid-session.

The signaling server failure mode is worth thinking about early. If the signaling server goes down after a connection is established, the peer connection continues - WebRTC is resilient once connected. But any reconnection attempt during an outage will fail silently from the user's perspective.

Mobile Is a Different Problem

WebRTC on Android and iOS works, but the implementation details diverge from the browser significantly enough that treating mobile as "the same as web" is a mistake.

Browser WebRTC is implemented natively. Mobile WebRTC uses the libwebrtc library, which requires integration effort. The library handles codec negotiation, but the specifics of codec preference, bandwidth adaptation behaviour and how the library interacts with the platform's audio session APIs vary enough between Android and iOS that you should budget separate debugging time for each.

iOS has specific requirements around audio session interruptions - phone calls, system notifications, AirPods switching - that need explicit handling. Getting this wrong produces audio dropouts that are difficult to diagnose because they are triggered by external events rather than your code.

Browser Compatibility Is Still Not Uniform

Chrome, Firefox and Safari all implement WebRTC but they don't implement it identically. Safari has historically lagged in WebRTC support and has specific behaviours around SDP format, codec support and permission handling that require browser-specific treatment.

Test your implementation in all target browsers before declaring it done. The cases that fail in Safari specifically tend to be subtle - the connection succeeds but audio is muted, or screen sharing doesn't work - rather than outright failures that are easy to catch.

What a Production WebRTC Architecture Looks Like

A minimal production setup requires:

  • Signaling server with connection state handling, reconnect support and message queue for out-of-order delivery
  • STUN server (free - use Google's or run your own)
  • TURN server with adequate bandwidth allocation, monitoring and geographic placement near your users
  • ICE configuration that gracefully falls through from STUN to TURN without user intervention

Beyond minimal:

  • Session recording via a media capture server, required in many regulated industries
  • SFU or MCU for group calls beyond 2-3 participants - a peer mesh doesn't scale
  • End-to-end encryption via the Insertable Streams API for encrypted media pipelines

WebRTC is genuinely powerful and browser support is good enough for production use. The gap between a tutorial demo and a production deployment is mostly operational - infrastructure, failure handling and mobile edge cases - rather than protocol-level complexity. Plan for it.