How a Pub/Sub Backplane Scales WebSockets Across Servers
Scaling WebSockets past one server needs a pub/sub backplane so clients on different machines still get every message. Here is how the backplane pattern works.
The moment you run more than one WebSocket server, you have a problem the single-server version hides: two clients in the same room might be connected to two different machines, and a message sent to one machine never reaches the other. Your chat splits in half. The fix is a pub/sub backplane, a shared channel that every server subscribes to, so that when any server receives a message it publishes it to the backplane and every other server delivers it to its own connected clients. This one pattern is what turns a single-box real-time app into one that scales horizontally, and understanding it is the difference between an app that grows and one that hits a ceiling at one machine.
Why one server is a ceiling
A single WebSocket server holds all the connections in memory, so when a message arrives it already knows every client in the room and can deliver to all of them directly. Simple, and it works right up until one machine cannot hold the load. Then you add a second server and put a load balancer in front, and clients get distributed across both. Now room membership is split: some members are on server A, some on server B. A message that lands on server A gets delivered to A's members and vanishes for B's members, because A has no idea B's clients exist. The room is broken.
You cannot solve this by making clients in the same room stick to the same server, at least not generally. Rooms overlap, users belong to many rooms, and sticky routing by room falls apart the instant a user is in two rooms hosted on different machines. You need the servers to talk to each other. That is what the backplane is for.
What the backplane does
A backplane is a shared pub/sub system that all your WebSocket servers connect to. The flow is: a message arrives at whichever server holds that client's connection, that server publishes the message to the backplane on the relevant channel, and every server subscribed to that channel receives it and delivers it to its own locally connected members. No server needs to know which clients live on which other server. Each one just handles its own connections and trusts the backplane to carry messages between machines.
This cleanly separates two jobs. Holding connections and delivering to local clients is the WebSocket server's job. Moving messages between servers is the backplane's job. That separation is what makes the system scale: you can add more WebSocket servers to hold more connections, and as long as they all share the backplane, every message still reaches every member of a room regardless of which machine they landed on. It is the concrete mechanism behind the horizontal scaling I described in scaling WebSockets.
The backplane can become the bottleneck
The backplane solves the correctness problem, but it introduces a new scaling question, because now every message flows through it. If every message goes to every server, and most servers hold no members of that room, you are doing wasted fan-out at the backplane level. For a lot of apps this is totally fine; the backplane is fast and the volume is manageable. But for large deployments it matters, and the way you manage it comes back to rooms and fan-out: the backplane load is driven by message rate times the number of servers that need each message, so keeping channels scoped and rooms reasonably sized keeps the backplane healthy.
A few big rooms are the stress case again. A message to a huge room has to reach every server holding any of its members, which in a large deployment is most of them. That is inherent to broadcasting widely, and it is why very large rooms get special treatment rather than riding the normal path. For the common case of many small rooms, the backplane barely notices, because each message only needs to reach the handful of servers that actually hold that room's members if your backplane supports scoped delivery.
Do not build this yourself unless you have to
A pub/sub backplane sounds simple in a diagram and is genuinely tricky in production: ordering across the hop, delivery guarantees, backpressure between the backplane and each server, failover when a server dies mid-delivery. These are the same hard problems I keep coming back to, like message delivery guarantees, now spread across multiple machines, which is strictly harder. Most teams should not hand-build this. I run real-time workloads on AltoHost precisely so the backplane, the horizontal scaling, and the cross-server delivery guarantees are the platform's problem, and I get to think in terms of rooms and messages instead of nodes and gossip. If you are deciding whether to own this layer, I worked through the tradeoff in build vs buy real-time infrastructure.
The signal that you have this right is boring: you add a second server, then a fifth, and rooms keep working exactly as they did on one machine, with no split conversations and no missing messages. If adding a server breaks delivery, you have no backplane, or a broken one. Get the backplane right and horizontal scale stops being scary; it becomes just adding boxes.