Team chat you actually own.
Fluid Chat is a free, open source alternative to Slack. Channels, DMs, threads, reactions, files, search and presence — hosted for you, or running on your own server against your own Postgres.
No credit card. No sales call. No seat count.
Channels
Direct messages
Ship logs, review requests, incident chatter
Migration is live on staging — realtime fan-out is going through Redis now, so we can run more than one app process.
@ada nice. I moved the retention notes into #incidents so they stay with the postmortem.
fluid-chat · production · a3f91c2 deployed in 42s
Search operators are landing too — from:@grace has:file in:#incidents
Katherine is typing…
Thread
#engineering
Moved the retention notes into #incidents.
4 replies
Set the channel policy to 90 days? Exports already cover audit.
Yes — and pin the runbook so new folks find it.
- $0
- per seat, forever
- ∞
- message history
- 1
- hard dependency: Postgres
- ~2 min
- to a running workspace
What you get
The features you use every day.
None of the ones you don’t.
Fluid Chat is not a toy clone. It is the working half of Slack — the messaging, awareness and search that a team lives in — built properly and shipped in full.
Channels, DMs and threads
Public and private channels, a channel browser, 1:1 and group DMs up to nine people, and threads with reply counts, participant facepiles and “also send to channel”.
- Archived channels
- Deterministic group DMs
- Thread following
Realtime that degrades gracefully
Writes go over HTTP, reads and events over WebSocket. Typing indicators, presence and live updates arrive instantly — and if the socket dies, the app keeps working.
- Optimistic sends
- Redis fan-out
- Idempotent delivery
Unreads worth trusting
Unread counts, mention badges, a “new messages” divider and mark-unread from any message. The activity feed collects mentions, thread replies, reactions and keyword hits.
- All unreads view
- Keyword highlights
- Quiet hours
Files, pins and Later
Drag, drop or paste files with inline image rendering and per-conversation file lists. Pin what matters, save messages to Later, set reminders, schedule sends for the morning.
- Permalinks
- Scheduled send
- Message forwarding
Slash commands and webhooks
Nineteen slash commands cover the day-to-day, and incoming webhooks let CI, alerting and scripts post into a channel under their own app identity.
- /remind · /dnd · /status
- Per-channel integrations
- App identities
A client people want to use
Light and dark themes, comfortable or compact density, ⌘K quick switcher, full keyboard shortcuts, custom emoji, sidebar sections — and a drawer layout that works on a phone.
- ⌘K quick switcher
- 12h/24h clocks
- Responsive by default
Search
Every message, forever, in milliseconds.
Postgres full-text search with the operators you already have muscle memory for — and no 90-day wall in front of your own history. Sort by recency or relevance.
in:#channelscope to a channelfrom:@personnarrow to a senderhas:fileonly messages with uploadshas:linkonly messages with linksis:pinnedsearch the pinned setbefore:2026-01-01everything olderafter:2025-06-01everything newerduring:2025-11a single month or day
Postmortem draft attached — the retention window is the open question.
Uploaded the timeline export so we can annotate it together.
Old runbook for the failover drill, keeping it here for reference.
Administration
Grown-up controls, without the enterprise tier.
The governance features other tools reserve for the plan above yours ship in the box, because there is no plan above yours.
- Roles that mean something
- Owner, admin, member and guest. Guests are scoped to the channels they’re added to — they can’t browse, join, invite or wire up integrations.
- Retention you set, per channel
- Workspace defaults with per-channel overrides, enforced by the background worker rather than by hoping people delete things.
- Exports that survive you
- JSONL plus CSV plus a file manifest. Greppable, diffable, and readable without Fluid Chat — messages store stable entity tokens, so renames never rewrite history.
- An audit log for the awkward questions
- Who changed the posting policy, who converted the channel to private, who removed the member — with the workspace-scoped queries to prove it.
Comparison
The same workday, a very different bill.
The messaging your team already knows — without the history limit, the per-seat invoice, or a cloud you don’t control.
Fluid Chatself-hosted | Slack Free | Slack paid | |
|---|---|---|---|
| Message history | Unlimited | Last 90 days | Unlimited |
| Cost per person | $0 | $0 | Monthly, per seat |
| Where your data lives | Your server | Their cloud | Their cloud |
| Source code | All of it, open | ||
| Threads & reactions | |||
| Search operators | |||
| Guest accounts | |||
| Audit log | Higher tiers | ||
| Custom retention policies | |||
| Full data export | Limited | ||
| Incoming webhooks | |||
| Huddles, voice & video | |||
| Native mobile apps | Responsive web | ||
| SSO / SAML |
Slack plan details are summarised from Slack’s published plans and can change — check slack.com/pricing for current terms. Slack is a trademark of Salesforce; Fluid Chat is not affiliated with them.
Pricing
Free. Not free-for-now,
free-for-everything.
All 91 features are in the free product, because there is no other product. Nothing is held back for a plan that costs money, because there is no plan that costs money.
per month
Every feature, for everyone on your team, with no limit on how long you keep your history. There is no second tier to move up to — this is the whole product.
No credit card. No sales call. No seat count.
Unlimited, and never counted
- People in your workspace
- Messages you send
- Message history you keep
- Files you upload
- Channels you create
- Integrations you connect
- Admin and governance features
- API calls you make
Things that do not exist here
- No per-seat invoice
- No 90-day history limit
- No feature gated behind a tier
- No trial that expires
- No credit card, ever
- No sales call
Self-host
Boring infrastructure, on purpose.
Postgres is the only hard dependency. Redis is optional — you need it once you run more than one app process. There is no proprietary datastore to learn and nothing to phone home to.
# clone, configure, run$ git clone https://github.com/azianmike/fluid-chat.git$ cd fluid-chat && cp .env.example .env$ docker compose up app ready on http://localhost:3000 realtime websockets on :3001 worker jobs running postgres migrations applied$ cp .env.example .env$ npm install$ npm run db:migrate$ npm run dev # app on :3000$ npm run realtime # websockets on :3001$ npm run worker # scheduled jobs# the first workspace you create makes you its owner,# with #general and #random ready to go.Next.js 15
App and API. One thin adapter over a typed server router.
Postgres
The only hard dependency. Messages, search index, everything.
Socket.IO
A small realtime server for presence, typing and event relay.
A worker
Scheduled sends, reminders, retention, exports, email.
Questions
The obvious ones, answered.
Is it actually free, or free-until-you-grow?
Free. You run it on your own hardware, so there are no seats to count, no licence keys and nothing to buy. The seat-limit and billing fields in the schema exist for anyone who wants to run Fluid Chat as a hosted service — they’re inert in a self-hosted install.
Can I import my Slack history?
Not yet — a Slack importer isn’t built. Fluid Chat exports its own data as JSONL, CSV and a file manifest, so getting data out is solved; getting Slack’s data in is the open piece. It’s a good first contribution if you need it.
What does it need to run?
Node 20+, Postgres 14+, and a place to put uploads. Redis is optional and only becomes necessary once you run more than one app or realtime process, at which point realtime events fan out over Redis pub/sub instead of a direct HTTP post.
Does it work on a phone?
Yes — the client uses a drawer layout that makes the whole app usable on a small screen. There are no native iOS or Android apps, and that’s a deliberate scope decision rather than a roadmap item.
How safe is my data?
It never leaves your server. Passwords are hashed with Argon2, every query is workspace-scoped through permission helpers rather than ad-hoc filters, and guests are confined to the channels they’re added to. Retention, exports and the audit log are yours to configure.
What happens if the websocket server goes down?
The app keeps working. Writes go over HTTP and realtime is treated as an accelerator, not a dependency — you lose live updates and typing indicators, not the ability to send and read messages.
Can I extend it?
That’s the design. Adding an endpoint is a route entry plus a service function, messages store stable entity tokens so renames never rewrite history, and the client renders from DTOs so the wire format is a deliberate contract.
Something not covered? Open an issue.
Move your team off the meter.
Start free in your browser, or clone it onto your own Postgres — either way, your company owns every message in it.