# Deploy the server

> What the server is, the one port it needs, and four ways to run it: Docker, Compose, Railway or Coolify.

Source: https://baalda.com/docs/deploy
Site: Baalda, https://baalda.com

The server is open source under Apache-2.0 and there is no paid tier for running it yourself. It is one Node service plus Postgres. Attachment bytes can live in Postgres too, so a working deployment can be two containers.

## Ports

| Variable | Default | What it serves |
|---|---|---|
| `PORT` | 3010 | The REST and auth API, the per-note sync WebSocket at `/sync`, and the vault channel at `/vault-sync`. This is the only port a deployment needs to expose. |
| `HOCUSPOCUS_PORT` | 3011 | A legacy dedicated port for the editing socket, used in local development and by much older clients. Not required in production. |

## Option A: plain Docker

Build the image from the repository root, not from the server directory. The server is one workspace of a pnpm monorepo and needs the workspace root's `package.json`, `pnpm-lock.yaml` and `pnpm-workspace.yaml` to resolve its dependencies.

```bash
docker build -f app/apps/server/Dockerfile -t baalda-server .
docker run -p 3010:3010 \
  -e DATABASE_URL=postgres://… \
  -e JWT_SECRET="$(openssl rand -base64 32)" \
  -e BETTER_AUTH_URL=https://notes.example.com \
  baalda-server
```

## Option B: Docker Compose

The bundle in `deploy/compose` brings up the server and Postgres together, and its README covers TLS, backups and upgrades.

```bash
cd deploy/compose
cp .env.example .env    # POSTGRES_PASSWORD, JWT_SECRET, BETTER_AUTH_URL
docker compose up -d
```

## Option C: Railway

One click provisions Postgres, generates its own signing secret, runs the migrations as a pre-deploy step with a healthcheck, and hands you an HTTPS URL. The repository carries the infrastructure definition, so a deployment is reproducible rather than clicked together.

## Option D: Coolify

For a self-managed platform. Same image, same variables, with the deployment document covering the specifics of wiring the database and the healthcheck.

## After it is up

1. Check `GET /health`. It answers with the version and the feature list the desktop app reads.
2. Set the server URL in the app, or send your team `https://<your-server>/open/connect`.
3. Decide where attachment bytes live before people upload much: Postgres needs nothing, S3 needs a bucket and a CORS policy.
4. Set up outbound email if you want password reset, verification and mailed invitations.

> Update the server before the desktop app, not after. A newer app against an older server shows a notice that the backend is behind, and some features stay off until the server catches up.

- [Configuration](/docs/configuration): Every environment variable, grouped by what it affects.
- [Connect clients](/docs/connect-clients): Pointing the app at your server, and the URL rules that apply.
