Skip to main content
WebSocketlesson 3 of 4 · 2 min read

Scaling WebSocket Fleets

The connection is the state

Scaling WebSocket is managing state, because the connection is the state.

Anna's socket lives on exactly one of your servers, so any message meant for Anna has to reach that specific machine.

Pick one of two architectures. A pub/sub backbone, where every server subscribes to channels for the users it holds and producers publish without knowing your topology. Or a registry mapping each user to their server, so senders route directly. Pub/sub is simpler and where nearly everyone starts.

Provision by connection count rather than request rate. Budget tens of kilobytes of memory per idle connection, and a tuned machine holds hundreds of thousands, though your framework's defaults will stop you far earlier.

Raise your file handle limits, your per-process memory, and your balancer's connection ceiling explicitly. And autoscale on connections and memory, because your processors stay misleadingly quiet on a fleet that mostly holds idle sockets.

Deploys are the recurring incident

Plan your deploys carefully, because this is the recurring incident. Restarting a server severs every connection it holds, and those clients all reconnect at once, hammering your auth and your balancer, sometimes toppling the next server and cascading.

Follow the playbook. Drain servers gradually, closing connections yourself over several minutes. Scatter your client backoff randomly. Make reconnect auth cheap. And hand each client a resume token so coming back skips the expensive session setup.

Watch connection churn as a first-class number, connects and disconnects per second rather than only how many you hold. A healthy fleet churns slowly. Churn climbing while your concurrent count stays flat means clients somewhere are stuck in a reconnect loop, quietly spending your auth budget.

the shape of it
AnnaWS server 1holds AnnaRedis pub/subWS server 2holds BenBenmsg for Benpublishfan outpush
step 1 of 4
Anna and Ben sit on different servers, so every message crosses a shared bus to find the right socket.

Worked example

A sports betting app holds 280,000 WebSocket connections across 14 pods during a big match. A routine Friday deploy rolls pods one at a time, each restart dumping 20,000 clients who all reconnect within 2 seconds. The auth service, doing a full token check against Postgres, the main database, on every connection, spikes to 10 times baseline and starts timing out, which makes clients retry harder. The rolling deploy takes down live odds for 6 minutes mid-match, the worst possible minutes in that business. The rebuilt pipeline drains each pod over 5 minutes with server-sent close frames, clients back off with 0 to 8 seconds of jitter, and reconnect auth validates a cached signed token with no database hit. The same deploy during the next derby produces a graph nobody can find the deploy on.