Baalda

Configuration

The environment variables the server reads: the required three, then sync, batching, storage, email, billing and scale.


Everything is read from the environment at startup. Only three variables are required. The rest have defaults that suit a single-instance deployment.

Required

VariableDefaultWhat it is
DATABASE_URLdev-only local valuePostgres connection string.
JWT_SECRETdev-only insecure defaultSigns sessions and sync tokens. Generate one with openssl rand -base64 32. Rotating it invalidates every session and sync token.
BETTER_AUTH_URLhttp://localhost:3010The public base URL used in auth links such as verification and invitations. It must match the URL clients actually use.

Serving and sync

VariableDefaultWhat it is
PORT3010HTTP API port, which serves both WebSocket paths as well.
HOCUSPOCUS_PORT3011Legacy dedicated editing-socket port.
VAULT_SYNC_PATH/vault-syncPath of the background vault channel on PORT.
CORS_ORIGINSunsetComma-separated origins allowed to call the API.
SYNC_TOKEN_TTL_SECONDS600Lifetime of a per-document sync token.
COMPACTION_THRESHOLD50Pending CRDT updates before the server compacts them into a snapshot.
PG_POOL_MAX30Connections this instance pools. Keep the total across instances under the database's own limit, and never below BACKFILL_CONCURRENCY.

Batching and first sync

These bound how much one request may carry. The desktop app chunks to them, so raising them is rarely necessary and lowering them is a safe way to protect a small instance.

VariableDefaultWhat it is
BATCH_MAX_NOTES200Notes per batch request. Past it the request is refused as too large.
BATCH_MAX_FOLDERS500Folders per batch. Higher because a folder row is cheaper.
BATCH_MAX_FILES200Files per batch.
BATCH_MAX_DOCS100CRDT content items per batch.
BATCH_MAX_DECODED_BYTES4 MiBTotal decoded update bytes one content batch may carry. This is the heap bound behind the request body limit.
BOOTSTRAP_MAX_PAGE_BYTES4 MiBByte budget for one bootstrap page. A single document larger than a page ships alone rather than being refused.
BOOTSTRAP_MAX_PAGE_DOCS256Document budget for one bootstrap page.
BOOTSTRAP_CONCURRENCY4Bootstrap pages built at once per instance. Past it a request answers 503 with a Retry-After. Raise it only alongside the container's memory.
BOOTSTRAP_TTL_HOURS24How long a bootstrap session stays valid before the client must start again.
BACKFILL_CONCURRENCY6Documents streamed at once to a freshly connected vault subscriber.

Attachments

Summarised here, with the full behaviour on the attachment storage page.

VariableDefaultWhat it is
BLOB_STORAGEpostgrespostgres or s3. An unrecognised value, or s3 with an incomplete bucket configuration, is a fatal startup error.
MAX_BLOB_BYTES25 MBCeiling for one attachment on Postgres.
MAX_BLOB_BYTES_DIRECT500 MBCeiling for one attachment on S3.
MAX_INFLIGHT_UPLOAD_BYTES2x MAX_BLOB_BYTESUpload bytes admitted at once. Beyond it uploads queue, then shed with a 503.
BLOB_MIME_ENFORCErejectreject answers 415 for an unknown content type. warn logs and stores it.
BLOB_PENDING_TTL_MINUTES60How long an abandoned upload holds its dedupe slot.
BLOB_GC_ENABLEDfalseWhether unreferenced attachments are collected.

Email

Optional, and off unless you configure it. See the email page for what each transport needs.

VariableDefaultWhat it is
EMAIL_FROMunsetSender address. With this and one transport, password reset, sign-up verification and invitation emails switch on.
SMTP_URLunsetAny SMTP server, with STARTTLS or TLS.
RESEND_API_KEYunsetThe HTTPS alternative to SMTP.
EMAIL_TRANSPORTinferredForce a transport instead of inferring it. log and memory exist for development and are refused in production.

Billing

Leave every billing variable unset and billing is fully disabled: no upgrade interface in the clients, no free-tier limits, and every self-hosted vault unlimited. Setting them turns on subscriptions through Polar.

VariableDefaultWhat it is
POLAR_ACCESS_TOKENunsetTurns billing on. Unset means disabled.
POLAR_WEBHOOK_SECRETunsetSigning secret of the webhook endpoint pointed at /api/billing/webhook.
BILLING_MODELvaultvault for per-vault plans with the free caps, or team for one account sold per seat.
TEAM_PRICE_MONTHLY_CENTS / TEAM_PRICE_YEARLY_CENTS1000 / 11000Per-seat prices shown in the app. Polar's product price is what is actually charged.
TEAM_MIN_SEATS3Smallest seat count a checkout accepts.
FREE_MAX_VAULTS / FREE_MAX_MEMBERS / FREE_MAX_STORAGE_MB2 / 3 / 1024Per-vault model only. Free-tier caps, enforced only when billing is on.
ABUSE_MAX_NOTES / ABUSE_MAX_STORAGE_MB100000 / 10240Team model only. Per-vault ceilings for free accounts. Never applied with billing off.

Other

VariableDefaultWhat it is
REDIS_URLunsetUnset means a single instance with in-memory fanout. Set it to run several instances. See scaling.
BAALDA_DEPLOYMENTcloudself-hosted lets people use their own model keys for server-side AI without any billing setup. cloud requires a paid plan for it.
DEEP_LINK_SCHEMEbaaldaThe app scheme the server's pages bounce into. Set baalda-staging on a staging server so links open the right build.
OPENAI_API_KEYunsetOptional embeddings for semantic search. The server works fully offline without it.
The deployment document in the repository carries the complete table, including the garbage-collection schedule and every value not listed here.

This page is written from docs/DEPLOY.md in the repository. Those documents go further, and they are the ones that change first.

Read it on GitHub