A multi-instance WebSocket chat server built with Rust (Actix Web), PostgreSQL, Redis pub/sub, and Nginx. Supports chat rooms, persistent event logging, and real-time event replay across multiple server nodes.
You can also monitor live events using RedisInsight and query the event log directly from PostgreSQL.
- Rust (1.72+ recommended)
- Docker & Docker Compose
- SQLx CLI
- PostgreSQL & Redis (only needed if running outside Docker)
git clone https://github.com/tahmid56/websocket-horizontal-scale.git
cd websocket-horizontal-scaleCopy and edit the environment file:
cp .env.local .envThe .env file should contain:
# PostgreSQL
DATABASE_URL=postgres://postgres:postgres@localhost:5432/chat
# Redis
REDIS_URL=redis://127.0.0.1:6379Note: When running via Docker Compose, the service hostnames (
postgres,redis) are used automatically — no manual editing needed.
# Install SQLx CLI (PostgreSQL feature only)
cargo install sqlx-cli --no-default-features --features postgres
# Create the database
sqlx database create
# Run migrations
sqlx migrate runThis starts two WebSocket server instances (ws1, ws2), PostgreSQL, Redis, and Nginx:
docker compose up -d --buildThe Nginx load balancer will be available at ws://localhost:8080.
To build locally without Docker:
cargo build --releaseInstall the WebSocket CLI client:
npm install -g wscatConnect to the server:
wscat -c ws://localhost:8080/wsOnce connected, send JSON commands:
Create a room
{"user_id": "1", "type": "create", "room": "general"}Join a room
{"user_id": "1", "type": "join", "room": "general"}Send a message
{"user_id": "1", "type": "message", "room": "general", "message": "Hello World!"}Leave a room
{"user_id": "1", "type": "leave", "room": "general"}| Component | Purpose |
|---|---|
| Rust WebSocket Server (Actix Web) | Handles client connections, WebSocket communication, and message broadcasting |
| PostgreSQL | Stores all chat events persistently — every create/join/leave/message is logged |
| Redis | Acts as a pub/sub message bus between server instances, keeping all nodes in sync |
| RedisInsight | Optional GUI for monitoring Redis channels and pub/sub events in real time |
| Nginx | Load balancer that distributes incoming WebSocket connections across server instances |
| Clients (wscat / Browser) | Connect via WebSocket, send commands, and receive real-time messages |
+------------------+ +------------------+
| Client 1 | | Client 2 |
| (wscat/browser) | | (wscat/browser) |
+--------+---------+ +--------+---------+
| |
| WebSocket (ws://) |
v v
+------------------+ +------------------+
| Rust WS Server 1 | | Rust WS Server 2 |
+--------+---------+ +--------+---------+
| |
+------- Redis Pub/Sub -----+
|
v
+----------------+
| Redis |
+----------------+
|
v
+----------------+
| PostgreSQL |
| (event log) |
+----------------+
-
Client connects via WebSocket and sends a JSON command (create, join, leave, or message).
-
Server handles the command — updates in-memory
ChatStatefor the room and broadcasts to connected clients in that room. -
Event is logged to PostgreSQL — every event is inserted into the
eventstable. New instances can replay missed events on startup. -
Event is published to Redis — all other server instances receive the event via the
chat:eventspub/sub channel, ensuring real-time sync across nodes. -
Other instances broadcast the event to their own local clients, completing the delivery.
-
(Optional) RedisInsight can subscribe to
chat:eventsto monitor events in real time.
.
├── src/ # Rust source code
├── migrations/ # SQLx database migrations
├── nginx/
│ └── nginx.conf # Nginx load balancer config
├── data/ # Persistent data (if any)
├── Dockerfile
├── docker-compose.yml # Orchestrates ws1, ws2, postgres, redis, nginx
├── Cargo.toml
├── .env # Production environment variables
└── .env.local # Local development environment variables