Scalable Chat System Using WebSockets, Load Balancer, Redis, and DynamoDB

In the world of modern chat applications, real-time communication is key. Whether it's for a messaging platform, collaborative tool, or gaming app, WebSockets offer a persistent and efficient channel for two-way communication. But what happens when you need to support thousands, or even millions, of users? Thatβs where a scalable architecture comes into play.
This blog will walk you through building a highly scalable chat microservice using WebSockets, Redis, DynamoDB, and a load balancer. Weβll explore how messages are routed, stored, and delivered efficiently across distributed services.
π Key Components
1. WebSocket Servers
These are the actual servers that handle WebSocket connections. Each server manages the live socket connections for the users it serves.
2. Redis (Pub/Sub + Socket Map)
Redis is used to keep track of where users are connected (i.e., which socket server they are on) and to publish/subscribe messages across different WebSocket servers.
3. DynamoDB (Persistent Storage)
Every chat message is stored in DynamoDB for durability and history retrieval.
4. Load Balancer
Distributes new incoming socket connections evenly across your pool of WebSocket servers.
πΊοΈ Architecture Diagram
ββββββββββββββ
β Client β
βββββββ¬βββββββ
β
βΌ
ββββββββββββββ
β Load Balancer
βββββββ¬βββββββ
ββββββββΌββββββββ
βΌ βΌ βΌ
ββββββββ ββββββββ ββββββββ
β WS1 β β WS2 β β WS3 β (WebSocket Servers)
ββββ¬ββββ ββββ¬ββββ ββββ¬ββββ
β β β
ββββββββββ΄βββββββββ
βΌ
ββββββββββ
β Redis β (Pub/Sub + Socket Map)
ββββββββββ
βΌ
ββββββββββββ
β DynamoDB β (Message Storage)
ββββββββββββ
π§© Setup Overview (Example):
Component | Address |
Load Balancer | ws:// chat.example.com |
WebSocket Server 1 | ws:// localhost:3001/ws |
WebSocket Server 2 | ws:// localhost:3002/ws |
WebSocket Server 3 | ws:// localhost:3003/ws |
Redis | redis:// localhost:6379 |
Message API | http://localhost:4000 |
DynamoDB | AWS managed |
πͺ User Connection Flow
1. User A opens a chat app
The frontend initiates a WebSocket connection:
const socket = new WebSocket("ws://chat.example.com/ws?userId=userA");
2. Load Balancer Routes the Request
The load balancer forwards this request to one of the WebSocket servers (e.g., WS1).
WebSocket handshake occurs.
3. WebSocket Server (WS1) handles the connection
Extracts
userId
from queryStores socket info in Redis:
SET user:userA { socketId: xyz123, server: ws1 }
π¬ Sending a Message: User A β User B
First Message:
User A sends:
{ "type": "message", "to": "userB", "message": "Hi" }
WS1 receives the message
WS1 looks up userB in Redis:
GET user:userB
Redis responds with
ws3
(userB is connected to WS3)WS1 publishes to Redis pub/sub:
channel: messages:ws3 payload: { "to": "userB", "message": "Hi", "from": "userA" }
WS3 receives it and sends it to userB via WebSocket
Message is also saved to DynamoDB
Second Message:
No new socket, no new Redis write
Uses existing socket to send again
Still does Redis lookup and pub/sub for delivery
π΄ User Goes Offline
1. WebSocket Disconnects
Server receives
socket.on('close')
Removes user from Redis
2. Another user sends them a message
Redis
GET
returns nullServer knows the user is offline
Stores message in DynamoDB with delivery status
3. User Comes Back Online
New WebSocket connects
Server updates Redis with new info
Checks for undelivered messages
Pushes them over the new socket
πͺ Is Load Balancer a Socket Server?
No. A load balancer is not a socket server. It only handles routing the initial HTTP/WS upgrade request to one of the WebSocket servers.
What the Load Balancer Does:
Routes initial WebSocket request
Uses round-robin or sticky session
After upgrade, client connects directly to WS server
All further communication happens directly
π Example With 10,000 Users
Infrastructure:
Component | Count |
Load Balancer | 1 |
WebSocket Servers | 3 |
Redis | 1 |
DynamoDB Table | 1 |
Load Balancer Behavior:
First user β WS1
Second user β WS2
Third user β WS3
Fourth user β WS1 (round-robin)
Each WS server handles ~3333 users, while Redis tracks all socket associations.
π¬ WebSocket Communication Endpoints
Endpoint | Method | Description |
/ws | WS | WebSocket upgrade endpoint |
messages:wsX | Redis PubSub | Channel for WS server wsX |
user:userId | Redis Key | Stores socket ID and server for user |
DynamoDB Table | - | Stores persistent messages |
π Horizontal Scaling and Communication Between Servers
Servers communicate using Redis Pub/Sub:
When WS1 needs to message a user on WS3, it publishes to
messages:ws3
channelWS3 is subscribed and receives it
No direct server-to-server calls are needed β Redis handles the communication.
β¨ Conclusion
This architecture enables your chat app to:
Scale horizontally
Deliver messages in real-time
Store persistent history
Handle user reconnects and offline messages
And with Redis + DynamoDB, you get both speed and reliability. The load balancer ensures users are evenly spread and connections stay persistent without needing to restart sockets.
If you're building a high-performance chat backend, this is the blueprint to follow.
Subscribe to my newsletter
Read articles from M S Nishaanth directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
