Skip to main content
Self-hosting puts your organization’s chat and calls on a server you control. One organization runs on one node.

What you need

  • A Linux host with Docker and Docker Compose.
  • A domain name pointing at the host (with subdomains for the media server and file storage).
  • Open ports for the web app and for real-time media (calls use UDP).

What’s in the stack

A single docker compose setup brings up everything the platform needs, so you’re not wiring services together by hand:

Bastion node

The API and real-time layer for chat, calls, keys and uploads.

Database & cache

PostgreSQL for durable data, Redis for presence and real-time fan-out.

File storage

S3-compatible object storage for encrypted attachments (bundled, or bring your own).

Media server

Carries encrypted call audio, video and screen share — on your infrastructure.

Background worker

A separate worker process delivers notifications and fans messages out to large channels, so it never competes with the API for responsiveness.

Bring it up

1

Generate config and secrets

2

Start the stack

3

Register your organization

Create the organization once, then invite members from the admin panel.

Configuration worth knowing

A few environment settings shape privacy and limits (sensible defaults ship out of the box):
  • MESSAGE_PUSH_HIDE_METADATA — keep notifications generic (no sender/chat identity in the push). On by default; set false for richer named notifications.
  • MAX_ATTACHMENT_BYTES — hard cap on a single attachment (default 100 MiB), enforced both up front and against the stored object.
  • FANOUT_MODE — dedicated (default in the prod stack) runs the separate worker container above; inline folds that work into the API for a minimal single-container deploy.
Organization-level privacy (metadata-minimal mode) is toggled from the admin panel, not the environment.

Secure by default

  • HTTPS is issued and renewed automatically by the bundled reverse proxy.
  • The database and file storage bind to localhost — never exposed to the open internet. Only the web app and media server are reachable from outside.
  • Fail-closed secrets: if a required secret is missing, the stack refuses to start rather than booting with a weak default.

What still depends on the control plane

Identity (passwordless sign-in), push notifications and licensing are brokered by the shared control plane, so accounts work across every app and across devices. Your content — messages, files and calls — never leaves your node; only that identity and licensing traffic goes to the control plane. See Accounts & privacy.
Don’t want to run any of this? The managed option is the exact same stack, operated for you. See Pricing.