How Do WebSockets Scale in a Distributed System?
Learn how WebSockets scale across servers using pub/sub fan-out, sticky sessions, and reconnect protocols.
Expected Interview Answer
WebSockets scale in a distributed system by keeping the persistent connection itself pinned to one server while offloading cross-server message delivery to a shared broker like Redis Pub/Sub or Kafka, so a message published anywhere can reach a client connected to any node.
A WebSocket is a long-lived, stateful, full-duplex TCP connection, which breaks the normal stateless-server assumption behind horizontal scaling: a client stays attached to whichever server accepted its handshake, and that server must hold the connection open (and its memory/CPU cost) for as long as the client is active. To fan messages out across many such servers, each instance subscribes to a shared pub/sub layer; when a message needs to reach a user, any server publishes it to the broker, and only the server actually holding that user’s socket delivers it locally. Load balancers must support sticky sessions or connection-aware routing (since a reconnect to a different node loses in-memory state), and connection counts per node become a first-class capacity metric, not just CPU or memory. At very large scale, teams shard users across WebSocket clusters by a consistent hash of user ID, and always design an authenticated reconnect-and-resume protocol, because networks drop long-lived connections constantly and clients must recover without losing messages sent during the gap.
- Decouples per-connection state from cross-server message delivery via a shared broker
- Lets each node scale independently while messages still reach the right client anywhere
- Turns connection count into a measurable, capacity-planned resource per node
- A resume protocol on reconnect prevents silent message loss during network drops
AI Mentor Explanation
A WebSocket at scale is like a stadium with several dedicated radio channels, where each spectator tunes into and stays locked onto one specific channel for the whole match instead of reconnecting every ball. If commentary needs to reach a spectator on channel three, the broadcast center pushes it onto a shared feed and only channel three’s transmitter actually beams it to that group. Spectators occasionally lose signal and must retune, picking up exactly where the commentary continues rather than missing the whole innings. That persistent per-channel connection plus a shared broadcast feed is exactly how WebSockets scale across many servers.
Step-by-Step Explanation
Step 1
Accept and pin the connection
A client’s WebSocket handshake lands on one server, which holds that connection’s state in memory for its lifetime.
Step 2
Publish cross-server messages to a broker
Any server that needs to reach a user publishes to a shared layer like Redis Pub/Sub or Kafka instead of contacting the user directly.
Step 3
Deliver locally on the owning node
Only the server actually holding that user’s socket receives the broker message and pushes it down the open connection.
Step 4
Handle reconnects gracefully
On disconnect, the client reconnects (possibly to a different node) and resumes via a session/resume token instead of losing in-flight messages.
What Interviewer Expects
- Explains that a WebSocket is stateful and pinned to one server, unlike stateless HTTP requests
- Names a cross-server fan-out mechanism (Redis Pub/Sub, Kafka, or similar)
- Discusses sticky sessions / connection-aware load balancing
- Addresses reconnect and message-resume handling for dropped connections
Common Mistakes
- Assuming a normal stateless load balancer setup works unchanged for WebSockets
- Forgetting that connection count per node is a first-class capacity metric
- Not designing a reconnect/resume protocol, causing silent message loss
- Ignoring how to fan a message out to a user who may be connected to any node
Best Answer (HR Friendly)
“WebSockets keep a connection open between a client and one specific server, which makes scaling tricky because that server has to remember the connection. To scale, we use a shared messaging layer so any server can send a message to a user, and only the server actually holding that user’s connection delivers it. We also make sure clients can reconnect smoothly and pick up any messages they missed.”
Code Example
const localSockets = new Map() // userId -> ws connection on THIS server
redisSub.subscribe("ws-fanout")
redisSub.on("message", (channel, raw) => {
const { userId, payload } = JSON.parse(raw)
const socket = localSockets.get(userId)
if (socket && socket.readyState === socket.OPEN) {
socket.send(JSON.stringify(payload)) // only the owning node delivers
}
})
function sendToUser(userId, payload) {
// any server instance can call this; it does not need the socket locally
redisPub.publish("ws-fanout", JSON.stringify({ userId, payload }))
}
wss.on("connection", (socket, req) => {
const userId = authenticate(req)
localSockets.set(userId, socket)
socket.on("close", () => localSockets.delete(userId))
})Follow-up Questions
- How would you shard millions of WebSocket connections across a cluster?
- How do sticky sessions at a load balancer interact with WebSocket scaling?
- How do you design a reconnect protocol that does not lose messages sent during a disconnect?
- What is the trade-off between WebSockets and Server-Sent Events for a notifications feature?
MCQ Practice
1. Why do WebSockets complicate standard horizontal scaling?
Unlike stateless HTTP requests, a WebSocket connection stays open on one server, so that server must hold its state for the connection’s lifetime.
2. What is a common way to deliver a message to a user connected to a different server node?
A shared broker like Redis Pub/Sub or Kafka lets any server publish a message that only the node actually holding the user’s socket delivers.
3. What should a robust WebSocket client do after an unexpected disconnect?
A resume protocol lets a reconnecting client recover from the last confirmed point, avoiding silent message loss from network drops.
Flash Cards
Why are WebSockets hard to scale horizontally? — Each connection is stateful and pinned to one server, unlike stateless HTTP requests.
How do you deliver a message to a user on another node? — Publish it to a shared broker (Redis Pub/Sub, Kafka); only the owning node delivers it locally.
What capacity metric matters most for WebSocket servers? — Open connection count per node, alongside CPU and memory.
What must a WebSocket client support for reliability? — A reconnect-and-resume protocol so dropped connections do not silently lose messages.