How to Scale WebSockets Across Multiple Servers
π― The Questionβ
"In a real-time chat application (like Slack or Discord), User A is connected via WebSocket to Server 1, and User B is connected to Server 2. When User A sends a message, how does Server 1 deliver it to User B when it has no direct access to User B's socket connection?"
β‘ 30-Second Elevator Pitchβ
Standard HTTP APIs are stateless: any server instance behind a load balancer can handle any request by reading from a centralized database.
WebSockets are stateful:
- When a client connects, an active TCP socket descriptor is held in that specific server's operating system RAM.
- If User A is on Server 1 and User B is on Server 2, Server 1 cannot write bytes to a socket held on Server 2.
The Solution is a Message Broker Backplane (Redis Pub/Sub / Kafka):
- Each chat room or channel maps to a pub/sub topic in Redis.
- When Server 1 receives a message from User A, it publishes the payload to the Redis topic.
- All backend servers subscribed to that topic receive the message over the internal network.
- Server 2 receives the event, looks up User B in its local in-memory socket map, and pushes the message down the client's TCP socket.
π§ Under-the-Hood: Redis Pub/Sub WebSocket Architectureβ
π¬ In-Memory Connection Routing Mapβ
Each WebSocket server maintains a thread-safe connection registry in memory:
// Local server RAM state
const localConnections = new Map(); // userId -> WebSocket connection
// When Redis delivers a message for room_101
redisSubscriber.on('message', (channel, message) => {
const { targetUserId, text } = JSON.parse(message);
// Check if target user has an active socket on THIS physical server
if (localConnections.has(targetUserId)) {
const clientSocket = localConnections.get(targetUserId);
clientSocket.send(text); // Direct TCP write
}
});
π Comparison Matrix: Stateless HTTP vs. Scaled WebSocketsβ
| Architecture Layer | Stateless HTTP Service | Clustered WebSocket Fleet |
|---|---|---|
| Connection Lifespan | Ephemeral (Opens, responds, closes) | Persistent TCP session (Minutes to hours) |
| Load Balancing | Standard Round-Robin / Least Connections | Requires Sticky Sessions (or Layer 4 IP Hashing) |
| Inter-Server Comm | None needed (Central database is enough) | Mandatory Message Broker Backplane (Redis / Kafka) |
| Reconnection Spike | Natural handling | Thundering Herd on server restart (Requires backoff jitter) |
| Server Failure | Client seamlessly hits next server on next call | Active socket dies; client must reconnect and re-subscribe |
π‘ What Interviewers Ask Next (Follow-Up Traps)β
-
"What happens if Redis crashes in a WebSocket cluster?"
- Answer: If the message backplane fails, inter-server communication breaksβusers on the same server can still chat, but cross-server messages are dropped. High-availability architectures use Redis Sentinel / Redis Cluster, or durable distributed commit logs like Apache Kafka / RabbitMQ to ensure high availability and message replay.
-
"Why do WebSocket Load Balancers require 'Sticky Sessions' during handshake?"
- Answer: A WebSocket connection begins as an HTTP request with an
Upgrade: websocketheader. The load balancer must route the HTTP upgrade request and all subsequent TCP packets to the exact same server instance to complete the handshake.
- Answer: A WebSocket connection begins as an HTTP request with an
Interview Answer: WebSockets cannot be scaled like stateless HTTP servers because socket descriptors are locked to an individual server's RAM. Scaled architectures connect all servers to a centralized message broker backplane (like Redis Pub/Sub), broadcasting messages across the fleet so each server can deliver events to its locally connected clients.
πΊ Video Explanationβ
Never Study in Isolation. Join Binary Dose.
Connect with fellow engineers, clear conceptual doubts, and get 2 visual breakdowns & engineering mental models daily across our community channels.
Discord Community
Start at #welcome-and-rules, say hello in #general-chat, clear doubts, and collaborate with engineers worldwide.