Skip to content

Environment variables

Every variable in website’s root .env.example — the file a Compose deployment actually reads, which is not the same file local development copies.

This list is checked against that file on every build, in both directions: a variable that disappears upstream fails, and a variable added upstream that is missing here fails too.

VariableWhat it is for
IMAGE_TAGWhich published image tag to run
NODE_ENVproduction or development — several refusals are production-only
PORTThe port the app listens on
INTERNAL_PORTThe internal-only listener, for the bot channel
UPLOAD_DIRWhere uploads are written
LOG_LEVELConsole log level
FILE_LOG_LEVELFile log level, set separately
LOG_TO_FILEWhether to write a log file at all
LOG_DIRDirectory for the log file
LOG_FILELog file name
BRAND_NAMESite name — branding is data, not a build
BRAND_SHORT_NAMEShort form, for tight spaces
BRAND_TAGLINEOne line under the name
BRAND_DESCRIPTIONMeta description
BRAND_CONTACT_EMAILPublished contact address
BRAND_URLCanonical public URL
BRAND_ACCENT_COLORAccent colour
BRAND_LOGOLogo path
BRAND_HEROHero image path
BRAND_FAVICONFavicon path
DB_HOSTDatabase host
DB_PORTDatabase port
DB_NAMEDatabase name
DB_USERDatabase user
DB_PASSWORDDatabase password
DB_ROOT_PASSWORDThe database container's root password
JWT_SECRETSigns session tokens. Rotating it logs everyone out
SECRET_ENC_KEYEncrypts secrets at rest. Required in production, and rotating it ORPHANS every stored secret
JWT_EXPIRES_INSession lifetime
COOKIE_SECUREauto decides Secure per request, so HTTPS and LAN HTTP both work
COOKIE_NAMESession cookie name. Changing it invalidates existing sessions
TRUST_PROXYNeeded behind a reverse proxy for secure cookies, real IPs and rate limiting
DEBUG_TRUST_PROXYDiagnostic for the above
TOTP_CHALLENGE_TTLHow long a pending 2FA challenge is valid
ADMIN_USERNAMEFirst admin, created only when no users exist
ADMIN_PASSWORDFirst admin password. Set it before the first boot, not after
CLIENT_ORIGINDev only — the Vite origin allowed through CORS
BOT_INTERNAL_URLWhere the Discord bot listens
BOT_INTERNAL_KEYAuthenticates the site↔bot channel. Required in production EVEN IF you run no bot
NTFY_BASE_URLPush notification relay base URL

SECRET_ENC_KEY encrypts secrets at rest — OAuth client secrets, the Discord bot token, the shard’s auth token. Changing it does not re-encrypt anything: what was stored under the old key can no longer be read, and every stored secret has to be entered again.

COOKIE_SECURE=auto decides Secure per request, which is what lets one deployment work both over HTTPS through a proxy and over plain HTTP on a LAN address. Forcing it either way breaks one of those.

TRUST_PROXY is required behind a reverse proxy for secure cookies, real client IPs and rate limiting to work at all. Without it, every request appears to come from the proxy — so rate limiting and IP bans apply to your whole user base at once.

A first install is Install the site, which prints a complete .env alongside its Compose file. Afterwards, Configuration covers what is env-configured and what is not.

Most settings are not here. Branding, navigation, theming and the shard connection are admin-managed and live in the database, deliberately — so changing them does not mean redeploying a container.

website’s .env.example is the authority, and BACKEND_DESIGN.md §8 covers deployment.