Senlo

Configuration

Every environment variable of a self-hosted instance, with its default.

Self-hosted only

These variables configure your own instance. Senlo Cloud is configured for you.

deploy/vps/.env (minimum)
DATABASE_URL=postgresql://senlo_user:senlo_password@db:5432/senlo_db
AUTH_SECRET="<openssl rand -base64 32>"
SECRETS_KEY="<openssl rand -base64 32>"
APP_URL="https://mail.example.com"

Every variable is read when the containers start, not when the image is built: change a value, then docker compose up -d, no rebuild. If a required value is missing or invalid, the app and the worker refuse to start and list every problem at once.

Required

VariableDefaultMeaning
DATABASE_URLnonePostgres connection string. env.example points at the bundled db container.
AUTH_SECRETnoneSigns sessions, and unsubscribe and tracking links unless LINK_SIGNING_SECRET is set. Generate with openssl rand -base64 32.
APP_URLhttp://localhost:3000 outside productionThe public address of the instance. Links in every email point here. Required in production. The old name NEXT_PUBLIC_APP_URL is still read.
SECRETS_KEYLINK_SIGNING_SECRET, then AUTH_SECRETEncrypts provider credentials in the database. Optional, but set it on the first install and never change it.

Optional

VariableDefaultMeaning
REDIS_URLredis://localhost:6379 (redis://redis:6379 in compose)Queues and rate limits.
REDIS_QUEUE_PREFIXsenloPrefix for queue and rate-limit keys, if several instances share one Redis.
LINK_SIGNING_SECRETAUTH_SECRETSigns unsubscribe and tracking links. Set it to rotate AUTH_SECRET (which signs everyone out) without breaking links in mail already sent.
DEFAULT_FROM_EMAILhello@senlo.ioSender for triggers that do not set one. Must be one plain address. Set it to an address on your verified domain.
ALLOW_REGISTRATIONtruefalse closes /register. Existing users still sign in.
INITIAL_USERS[]JSON array of users created on start, e.g. '[{"name":"Admin","email":"admin@example.com","password":"...","role":"ADMIN"}]'. They get example workspaces and templates.
GITHUB_ID, GITHUB_SECRETunsetEnable sign-in with GitHub when both are set.
AUTH_TRUST_HOSTtrue in composeLets Auth.js trust the Host header behind your reverse proxy.
APP_BIND127.0.0.1:3000Compose only: where the app's port is published on the host. Keep it on localhost behind a proxy.
IS_DEMO_MODEfalseOnly for a public demo instance: loads Umami analytics (with UMAMI_WEBSITE_ID).
UMAMI_WEBSITE_IDunsetUmami website id, used with IS_DEMO_MODE.

File storage

Images uploaded in the editor go to the app's disk (the uploads_data volume) by default. For S3 or an S3-compatible service (Cloudflare R2, MinIO):

VariableDefaultMeaning
STORAGE_DRIVERlocallocal or s3.
S3_BUCKETnoneRequired with s3.
S3_REGIONnoneRequired with s3. auto for R2.
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYnoneRequired with s3.
S3_ENDPOINTunset (AWS)The endpoint of an S3-compatible service.
S3_PUBLIC_URLhttps://<bucket>.s3.<region>.amazonaws.com on AWSWhere objects are served from. Required with S3_ENDPOINT.
S3_KEY_PREFIXemptyKey prefix inside the bucket, e.g. uploads.
S3_FORCE_PATH_STYLEtrue with S3_ENDPOINT, else falsePath-style addressing, which most S3-compatible services need.

Maintenance commands

CommandWhen
docker compose exec -e OLD_SECRETS_KEY="<old key>" app pnpm db:reseal-secretsRe-encrypts provider credentials after SECRETS_KEY changed.
docker compose exec app pnpm db:hash-api-keysHashes API keys stored in the clear by old versions.

See Production checklist for what to set before going live.

Edit this page on GitHub