Configuration
Every setting is read from .env at runtime, so changing a value and
restarting the affected containers applies it — no rebuild is needed. This page
covers the settings you must set by hand after running ./docker/gen-secrets.sh
(which handles the required secrets).
For the full list of variables, see the Environment variables reference. For on/off feature switches, see Feature toggles.
Application
Section titled “Application”| Variable | What it does |
|---|---|
APP_URL |
The public URL of your instance (e.g. https://chat.example.com). |
APP_NAME |
The app name shown in the UI and emails (default The Desk). |
APP_PORT |
Host port the web app is published on (default 8000, bound to loopback). |
APP_BIND |
Address the published app/reverb ports bind to (default 127.0.0.1). |
APP_NAME and the browser-facing Reverb settings are served to the frontend at
runtime, which is why one published image works for any host.
Mail (SMTP)
Section titled “Mail (SMTP)”The Desk sends workspace invitations (and email verification, if you
enable it), so SMTP must work.
Set the MAIL_* variables to your provider’s credentials:
MAIL_MAILER=smtpMAIL_HOST=smtp.example.comMAIL_PORT=587MAIL_USERNAME=postmaster@example.comMAIL_PASSWORD=your-smtp-passwordMAIL_FROM_ADDRESS="chat@example.com"MAIL_FROM_NAME="${APP_NAME}"Reverb (WebSockets) — mind the browser vs. server split
Section titled “Reverb (WebSockets) — mind the browser vs. server split”Reverb powers real-time updates. The setting that trips people up is that the container and the browser reach Reverb differently:
- The container speaks plain
httpon8080—REVERB_PORT/REVERB_SCHEME. - The browser reaches Reverb through your TLS proxy on
wss/443.
So set the browser-facing (*_PUBLIC) values accordingly:
REVERB_PORT_PUBLIC=443REVERB_SCHEME_PUBLIC=https# The browser-facing host defaults to your APP_URL host. Override only if you# serve Reverb from a dedicated WebSocket subdomain:# REVERB_HOST_PUBLIC=ws.example.comThese are read at runtime, so a restart applies changes — no rebuild.
Search (Meilisearch)
Section titled “Search (Meilisearch)”MEILISEARCH_KEY is a required secret (generated for you). MEILISEARCH_VERSION
pins both the image tag and the version-scoped data volume — see
Upgrading for why that matters.
Single sign-on (OpenID Connect)
Section titled “Single sign-on (OpenID Connect)”To let members sign in through your identity provider (Okta, Microsoft Entra ID,
Google Workspace, Auth0, Keycloak, …), register an OAuth application there with
the redirect URI https://your-host/auth/oidc/callback, then set:
SSO_OIDC_ISSUER=https://your-idp.example.comSSO_OIDC_CLIENT_ID=your-client-idSSO_OIDC_CLIENT_SECRET=your-client-secretA “Sign in with SSO” button appears on the login page. The first login
just-in-time provisions the account into the default team as a Member (matched to
an existing user by verified email). For the full list of options — default team,
scopes, and routing all access through the directory with AUTH_SSO_ONLY —
see Environment variables → Single sign-on
and Feature toggles → SSO-only mode.
Single sign-on (LDAP / Active Directory)
Section titled “Single sign-on (LDAP / Active Directory)”To authenticate members against an on-prem LDAP or Active Directory server, point the app at your directory and a read-only service (bind) account:
LDAP_HOST=ldap.example.comLDAP_PORT=389LDAP_BASE_DN="dc=example,dc=com"LDAP_USERNAME="cn=readonly,dc=example,dc=com"LDAP_PASSWORD=your-service-account-password# Encrypt the connection in production:LDAP_TLS=true # LDAPS (usually port 636)# LDAP_STARTTLS=true # or upgrade a plain connection insteadMembers then sign in with their directory credentials on the normal login form (the app binds to verify them — no browser redirect). The first login just-in-time provisions the account into the default team as a Member, matched to an existing user by the directory’s mail attribute and keyed by its stable objectGUID; the mapped display name syncs on every login.
By default members sign in with their email and the display name comes from
cn. If your directory uses different attributes — for example Active Directory
where users log in with sAMAccountName — remap them:
LDAP_ATTR_USERNAME=samaccountname # what members type on the login formLDAP_ATTR_NAME=displayname # attribute used as the app display nameLDAP_ATTR_GUID=objectguid # objectguid (AD) or entryuuid (OpenLDAP)See Environment variables → Single sign-on (LDAP) for every attribute mapping, and Feature toggles → SSO-only mode to require directory login.
Directory provisioning (SCIM 2.0)
Section titled “Directory provisioning (SCIM 2.0)”The sign-on options above provision an account the first time someone logs in. To also have accounts deactivated automatically when someone is removed from the directory, enable the SCIM 2.0 endpoint and let your identity provider push lifecycle changes to it. Set a bearer token:
SCIM_TOKEN=a-long-random-secret # blank keeps the endpoint off# SCIM_BASE_PATH=/scim # change the base path if you need toThen, in your IdP’s provisioning settings, point it at:
SCIM base URL: https://your-desk.example.com/scim/v2Auth: HTTP header — Authorization: Bearer a-long-random-secretThe IdP then keeps accounts in sync automatically:
- Create matches an existing account by email or provisions a new one into the default team as a Member — the same rules as OIDC/LDAP.
- Deactivate (
active: false, or aDELETE) tombstones the account: its sessions end immediately and it can no longer sign in, but its messages and history are kept rather than hard-deleted. - Reactivate (
active: true) restores access.
Members are matched on userName, which should be their email.
See Environment variables → Directory provisioning (SCIM 2.0) for the full reference.
Applying changes
Section titled “Applying changes”After editing .env, restart the stack to pick up the new values:
docker compose up -dThe bare docker compose needs no -f docker-compose.prod.yml because .env
sets COMPOSE_FILE. See
the COMPOSE_FILE variable.
If an edit still seems to have no effect, the running containers are serving a
boot-time config snapshot; add --force-recreate or see
Troubleshooting → Changed .env but nothing changed.