Most “best self-hosted X” lists are feature comparisons with the word self-hosted in the title. This one judges the thing that decides whether a self-hosted deployment survives its second year: how much operational surface you're agreeing to babysit.
Because feature lists don't page you. Services do.
The metric that matters: services to operate
Count the things that can independently break while people are trying to tell each other something is broken. That number, more than any feature, predicts whether you'll still be running this in eighteen months.
| Services in a default deployment | Database | Search | |
|---|---|---|---|
| Fluid Chat | App, realtime, worker, Postgres | Postgres | In Postgres |
| Mattermost | Server, Postgres | Postgres or MySQL | In Postgres; Elasticsearch at scale |
| Zulip | Server stack, Postgres, Redis, RabbitMQ, memcached | Postgres | In Postgres |
| Rocket.Chat | App, MongoDB, often more | MongoDB | Mongo text or Elasticsearch |
| Element / Matrix | Homeserver, Postgres, client, often workers | Postgres | Varies |
Two notes for fairness. Mattermost has the simplest raw deployment — one Go binary plus a database is hard to beat, and it beats Fluid on this row. Zulip's stack looks heavier than it is, because its installer sets the whole thing up for you on a fresh Ubuntu box and does it well; the count matters mainly when something goes wrong.
Resource requirements
Team chat is a low-throughput workload that looks like a high-throughput one. A 50-person team writes a few thousand messages a day, which is nothing. Where the platforms actually differ is their floor — the memory each wants before it will behave.
- Fluid Chat — a 1 vCPU / 2 GB box handles ~25 people with Postgres on the same machine.
- Mattermost — similarly modest; the Go binary is efficient.
- Zulip — documented minimums are higher, largely because of the supporting services.
- Rocket.Chat — MongoDB and the Node app together want more headroom than you'd guess from the user count.
- Element / Matrix — the heaviest, particularly once federation is doing real work.
Backups: the row nobody reads until they need it
A backup strategy is only as good as the number of places your data lives. One datastore is a pg_dump and an object-store sync. Two datastores is a consistency question you have to think about at exactly the wrong moment.
- 1Fluid Chat, Mattermost, Zulip — Postgres dump plus object storage. Standard, well-tooled, boring in the best sense.
- 2Rocket.Chat — MongoDB backup plus files. Perfectly workable, but a different skill set from the SQL tooling most teams already own.
- 3Element / Matrix — Postgres plus media store, with federation state adding subtlety.
Whichever you pick: restore it once, on purpose, before you need to. A backup you've never restored is a rumour with a filename. This is the single highest-value hour in any self-hosted deployment and almost nobody spends it.
Upgrade risk
The other thing that kills long-running self-hosted deployments is the upgrade nobody wants to run, which becomes the version nobody dares to change, which becomes the security problem.
- Migrations that run on start are the friendliest pattern — you take a backup, deploy, and the schema catches up. This is what Fluid does.
- Installer-driven upgrades (Zulip's model) are reliable and opinionated, and want the machine to look how the installer expects.
- Binary swaps (Mattermost) are about as simple as upgrades get.
- Anything with federation state deserves the most caution, because your upgrade interacts with other people's servers.
Picking, operationally
| If your situation is… | Pick | Because |
|---|---|---|
| Regulated, needs SSO and support | Mattermost | Enterprise features and a vendor with an SLA |
| Needs voice, video, omnichannel | Rocket.Chat | Nothing else self-hosted matches the breadth |
| Async across many timezones | Zulip | Topics make catching up genuinely tractable |
| Federation is the requirement | Element / Matrix | It's the protocol, not just an app |
| Small team, one box, no tiers | Fluid Chat | One database, three processes, nothing gated |
Note that three of those five rows point away from us. That's not modesty — self-hosting a tool that doesn't fit your requirements is how you end up running two chat systems, which is worse than either.
Installing Fluid, since you're here
# clone, configure, rungit clone https://github.com/azianmike/fluid-chat.gitcd fluid-chat && cp .env.example .envdocker compose up app ready on http://localhost:3000 realtime websockets on :3001 worker jobs running postgres migrations appliedPostgres is the only hard dependency. Redis becomes necessary only when you run more than one app process. Docs for SMTP, S3/MinIO, HTTPS, backup, restore and upgrade all ship in the repo.
One database. Three processes. About two minutes.
Or skip the server entirely — the hosted version is free too, with no seat count and no history limit.
The honest caveat about self-hosting
Every product on this list is good enough that the software won't be your problem. Your problem will be that the person who set it up changed teams, the TLS certificate renewal broke on a public holiday, and nobody has tested the restore.
So pick the one with the fewest moving parts that meets your requirements, write down how to restore it, and put a calendar reminder on upgrades. That's the whole discipline, and it's more important than which of these five you chose.