Set in .env, read by docker compose and by the app. Admin → Settings shows the configuration the environment supplies, so nobody has to read a compose file to see how an instance is running.
Core
| Variable | Required | Default | Description |
|---|
DATABASE_URL | yes | — | postgres://trove:<password>@db:5432/trove |
POSTGRES_USER | no | trove | Overridable for deployments with their own naming rules |
POSTGRES_DB | no | trove | |
POSTGRES_PASSWORD | yes | — | Must match DATABASE_URL |
AUTH_SECRET | yes | — | openssl rand -base64 32 |
APP_URL | yes | http://localhost:3080 | The public origin. Drives Secure cookies, OAuth state, absolute links |
APP_PORT | no | 3080 | Host port. The container always listens on 3000 |
APP_BIND | no | 0.0.0.0 | 127.0.0.1 keeps the port off the network behind a tunnel or proxy |
APP_LOCALE | no | en-US | Interface language for a new visitor: en-US or en-GB. Readers can switch |
SEED_ON_START | no | — | true loads the starter doc types on boot |
SEED_LOCALE | no | en-US | Wording of the seeded content |
CRON_SECRET | no | — | Lets a scheduler drive webhook and schedule passes through /api/internal/webhooks. Empty keeps the endpoint inert |
TROVE_DISABLE_WEBHOOK_WORKER | no | — | true turns the in-process retry worker off, for a second replica |
SCHEDULE_POLL_SECONDS | no | — | How often the schedule worker looks for what has come due |
Limits
| Variable | Default | Description |
|---|
APP_CPUS / APP_MEMORY | 2 / 1g | Container limits, so one busy upload cannot starve the host |
DB_CPUS / DB_MEMORY | 2 / 1g | |
MAX_UPLOAD_MB | 25 | Per-attachment limit. Keep it under your Cloudflare plan’s body cap |
KB_IMPORT_MAX_MB | 1024 | Largest knowledge base archive accepted |
KB_IMPORT_DIR | — | Scratch space for unpacking imports; articles live in the database |
KB_CONNECTOR_POLL_SECONDS | 300 | How often the worker looks for a knowledge base connector that is due |
Mail and sign-in
| Variable | Description |
|---|
SMTP_URL, MAIL_FROM | Outgoing mail for password reset links. Without them, a forgotten password is a temporary one from an administrator |
OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET | All three or none. Discovery is used; Entra ID, Google, Authentik, Keycloak all work. Redirect URI is <APP_URL>/api/auth/callback/oidc |
Attachments
| Variable | Default | Description |
|---|
STORAGE_DRIVER | local | local, s3, or r2 (Workers) |
STORAGE_PATH | /data/uploads | In-container path for local |
NFS_SERVER, NFS_UPLOADS_PATH | — | With docker-compose.nfs.yml, keeps the uploads volume on a NAS. The database stays on local disk either way |
S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY | — | Required when STORAGE_DRIVER=s3 |
Vault
| Variable | Default | Description |
|---|
VAULT_MODE | link | link, bw_serve, op_connect, or hashicorp_kv |
BW_SERVER_URL, BW_WEB_VAULT_URL | — | Vaultwarden, self-hosted Bitwarden, or https://vault.bitwarden.com |
BW_CLIENTID, BW_CLIENTSECRET | — | The service account’s API key, for bw_serve |
BW_SERVE_URL | http://bw-serve:8087 | The sidecar, on the internal network |
BW_SERVE_ACCESS_CLIENT_ID, BW_SERVE_ACCESS_CLIENT_SECRET | — | Only when the sidecar sits behind a Cloudflare Tunnel with Access |
BW_SYNC_INTERVAL_MIN | 5 | |
BW_PUBLIC_API_CLIENT_ID, BW_PUBLIC_API_CLIENT_SECRET | — | Bitwarden Public API add-on, Teams/Enterprise only |
OP_CONNECT_URL, OP_CONNECT_TOKEN | — | 1Password Connect, --profile onepassword |
HASHICORP_VAULT_ADDR, HASHICORP_VAULT_TOKEN, HASHICORP_VAULT_MOUNT | secret | HashiCorp KV v2 |
The Bitwarden master password goes in ./secrets/bw_master_password.txt (chmod 600), never in .env. The 1Password credentials file goes in ./secrets/1password-credentials.json.