> ## Documentation Index
> Fetch the complete documentation index at: https://docs.burakov.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting

> Run your own Bastion node — requirements, what's in the stack, TLS, and how it stays secure by default.

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:

<CardGroup cols={2}>
  <Card title="Bastion node" icon="server">
    The API and real-time layer for chat, calls, keys and uploads.
  </Card>

  <Card title="Database & cache" icon="database">
    PostgreSQL for durable data, Redis for presence and real-time fan-out.
  </Card>

  <Card title="File storage" icon="box-archive">
    S3-compatible object storage for encrypted attachments (bundled, or bring your own).
  </Card>

  <Card title="Media server" icon="video">
    Carries encrypted call audio, video and screen share — on your infrastructure.
  </Card>

  <Card title="Background worker" icon="gears">
    A separate worker process delivers notifications and fans messages out to large channels, so it never competes with the API for responsiveness.
  </Card>
</CardGroup>

## Bring it up

<Steps>
  <Step title="Generate config and secrets">
    ```bash theme={null}
    npm run setup      # strong, unique secrets + your domains, written into the environment
    ```
  </Step>

  <Step title="Start the stack">
    ```bash theme={null}
    docker compose --profile prod up -d
    ```
  </Step>

  <Step title="Register your organization">
    Create the organization once, then invite members from the [admin panel](/bastion/administration).
  </Step>
</Steps>

## 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](/bastion/administration), 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](/bastion/accounts-and-privacy).

<Note>
  Don't want to run any of this? The **managed** option is the exact same stack, operated for you. See [Pricing](/bastion/pricing).
</Note>
