Self-hosting a chat server: choices and architecture
Want to run your own chat server — DMs inside your app, community super-groups, or customer support on your own metal? The problems you must solve are the same every time. This page breaks them down, compares the three common routes, and gives a reference architecture with real capacity numbers using IMCore as the example, so you can decide whether to buy, assemble, or build.
1. What a chat server actually has to solve
Getting from “can send a message” to production-grade is at least these eight things:
- Connection gateway: WebSocket long connections at scale, heartbeats, reconnects, routing of connection state.
- Reliable delivery: ACKs, retries, deduplication and ordering — “an occasionally dropped message” is not acceptable in production.
- Offline and push: messages persist when the peer is offline; APNs / vendor channels wake backgrounded apps.
- Group fan-out: one group message must reliably fan out to thousands of member connections — the core throughput bottleneck of any IM system.
- History and storage: conversation history, server-side search, multi-device sync; object storage for files, images and voice.
- Presence and receipts: online/offline, typing indicators, read receipts — each is a state machine, not one broadcast.
- Multi-device and accounts: concurrent logins, kick-out, message roaming.
- Security and compliance: transport encryption (wss/TLS), auth (JWT), moderation hooks, retention policy.
Most “build a chat room in a weekend” tutorials stop at item 1. The engineering bulk of a production system lives in items 2–6.
2. Choosing among the three routes
| SaaS IM/support SDK | Open-source assembly (XMPP/Matrix/oss IM) | Self-hosted engine (IMCore et al.) | |
|---|---|---|---|
| Time to launch | Fastest — integrate the SDK | Slow — assembly plus lots of glue code | Fast — full server + client SDKs |
| Data sovereignty | Data in the vendor’s cloud | Fully yours | Fully yours |
| Cost model | Recurring MAU/seat fees | Free in theory, expensive in headcount | One-time license (with a free Basic tier) |
| Production hardening | Vendor’s job | Push, moderation, clustering, monitoring — all yours | Ships with the release |
| Customization depth | SDK boundaries | Unlimited (you write it) | Server APIs + UIKit at source level |
Rules of thumb:
- Demo phase / non-sensitive use → a SaaS SDK is fastest.
- Dedicated IM team evolving your own protocol long-term → open-source assembly or full self-build.
- Data must stay on your infrastructure yet you don’t want to reinvent the wheel (finance, healthcare, government, compliance-driven products, or chat as a core product feature) → a self-hosted engine is the “both” answer.
Head-to-head tables against specific competitors live on the comparison pages.
3. Reference architecture: single node to cluster
Single node (Basic edition):
clients ──wss──> [websocket server] ──> PostgreSQL (messages/accounts/history)
│
└──> Redis (presence / routing cache / session state)
One binary (or container) plus PostgreSQL and Redis is the complete server. This is the simplest deployment (see Production deployment with Docker Compose) and covers most small-to-medium self-hosted scenarios.
Clustered (Pro and up):
clients ──> [websocket × N] ──shared──> Redis (state)
│
└──> message queue (RabbitMQ / NATS / Redis Streams)
│
[persist_consumer × N] ──> PostgreSQL (batch writes)
Multiple instances fan out and persist through the queue and scale horizontally; every replica shares the same Redis and broker. No client code changes — the upgrade is purely a deployment change.
4. Capacity reference: measured numbers
Saturation test of a single IMCore instance (4C8G, multi-room scenario, 200 msg/s send rate):
| Concurrent connections | Fan-out | p50 | p99 | Errors |
|---|---|---|---|---|
| 5,000 | 19,222 msg/s | 52ms | 93ms | 0 |
| 10,000 | 38,506 msg/s | 83ms | 153ms | 0 |
One 4C8G instance carries ten thousand concurrent connections and nearly 40k group fan-out messages per second at p99 under 155ms; beyond that, scale out via the clustered topology. For capacity planning, estimate fan-out demand as “peak concurrency × group amplification factor” and compare against instance sizing.
5. Pre-launch security checklist
- Expose only 443 (wss/TLS); database, Redis and the queue stay on the internal network (the Compose guide shows the isolated topology).
- Clients authenticate with JWTs minted by your own backend; never ship the signing secret inside a client bundle.
- Daily database backups stored off-host; retention policy aligned with your compliance requirements.
- Wire moderation hooks into the send path before public launch (Pro includes moderation callbacks).
Next steps
- Get a message flowing in 10 minutes: Quickstart
- Production deployment: Docker Compose · Kubernetes (Helm)
- Client integration: Integration guide · SDK overview
- Try it live: Demo · Comparisons · Pricing