Self-hosting
Configuration
Every environment variable, and which install path uses it.
Configuration lives in environment variables. Docker installs use a single
root .env; local development uses per-app .env files.
Docker installs — root .env
Written by the installer; the annotated template is .env.example.
| Variable | Required | Description |
|---|---|---|
POSTGRES_PASSWORD | yes | Database password (generated by the installer) |
BETTER_AUTH_SECRET | yes | Auth signing secret, min 32 chars — placeholders are rejected |
BETTER_AUTH_URL / CORS_ORIGIN | production | Public API URL / web origin (derived from domains in the overlay) |
VITE_SERVER_URL / VITE_COLLAB_URL | production | URLs baked into the web bundle at build time |
WEB_DOMAIN, API_DOMAIN, COLLAB_DOMAIN, ACME_EMAIL | production | Domains + Let's Encrypt email for the Caddy overlay |
# Generate each secret with:
openssl rand -base64 48VITE_* values are compiled into the web bundle at build time. Changing one requires rebuilding
the web image: docker compose ... up -d --build web.
Local development — per-app .env
For pnpm dev, each app reads its own file.
apps/server/.env
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | yes | PostgreSQL connection string |
BETTER_AUTH_SECRET | yes | Auth signing secret (min 32 chars, no placeholders) |
BETTER_AUTH_URL | yes | Public URL of the server |
CORS_ORIGIN | yes | Allowed origin of the web app |
NODE_ENV | no | development (default) / production / test |
APP_NAME | no | Display name used by auth flows (white-labeling) |
RATE_LIMIT_MAX / RATE_LIMIT_AUTH_MAX | no | Per-IP requests/minute for the API / auth routes |
RATE_LIMIT_SCIM_MAX | no | Per-IP requests/minute for SCIM (default 1200) |
apps/collab/.env
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | yes | Same database as the server |
BETTER_AUTH_SECRET | yes | Same secret as the server (verifies collab tokens) |
COLLAB_PORT | no | WebSocket port (default 1234) |
apps/web/.env
| Variable | Required | Description |
|---|---|---|
VITE_SERVER_URL | yes | URL the browser uses to reach the API |
VITE_COLLAB_URL | yes | WebSocket URL of the collab service (ws(s)://) |
The collab service must share BETTER_AUTH_SECRET with the server — it verifies page-scoped
collab tokens signed with it. See Collaboration architecture.
Ports at a glance
| Service | Port |
|---|---|
| web | 3001 |
| server | 3000 |
| collab | 1234 |
| postgres | 127.0.0.1:5432 |
In production, only Caddy (80/443) is published; the app ports are internal.