Every environment variable the control plane reads, what it defaults to, and what happens if you get it wrong.
The control plane's configuration is one validated schema. Values are read in configuration
factories only — nothing else in the codebase touches process.env.
On a self-hosted install these live in /etc/sthora/.env, written by the installer at
mode 0600. In development they live in apps/backend/.env; only .env.example is ever
committed.
Two values have no safe default
DATABASE_URL and AUTH_SECRET. Generate the secret with openssl rand -hex 32 and never reuse a placeholder.
BullMQ stores job state in Redis. An LRU policy silently evicts it and
deployments disappear with no error anywhere. If you want a caching Redis with
eviction, that is a second instance or a second logical database with its
own policy — never the queue's.
Required, minimum 32 characters. Signs sessions and one-time tokens, and keys API-token digests.
AUTH_SESSION_TTL_HOURS
720
Absolute session lifetime.
AUTH_SESSION_RENEW_AFTER_HOURS
24
How old a session must be before a request slides its expiry forward.
AUTH_RESET_TOKEN_TTL_MINUTES
60
Password reset. Capped at 60 by the schema.
AUTH_VERIFICATION_TOKEN_TTL_MINUTES
1440
Email verification.
AUTH_INVITATION_TTL_HOURS
168
Invitation validity.
AUTH_COOKIE_NAME
cloud.session
The __Host- prefix is applied automatically in production.
AUTH_TRUSTED_ORIGINS
Empty
CSRF allow-list — origins whose unsafe-method requests carrying the session cookie the API will act on. Empty means same-origin only, which is the self-hosted default.
SIGNUP_ENABLED
false
Self-service signup. Off answers 404, not 403. See signup.
Comma-separated. Empty disables CORS, correct for a self-hosted install serving the dashboard and API on one origin.
THROTTLE_TTL_SECONDS
60
Global rate-limit window.
THROTTLE_LIMIT
120
Requests per window per client.
TRUSTED_PROXY_HOPS
0
How many reverse proxies sit in front. Set to 1 for the shipped compose topology, which puts Traefik in front.
TRUSTED_PROXY_HOPS matters more than it looks
It configures Express's trust proxy, which is what makes the rate limiter
and every audit record key on the real client's address rather than the
proxy's — or, left at 0, protects against a client forging its own address
via X-Forwarded-For.
Name the control plane's own machine is registered under on first boot.
METRICS_SAMPLE_INTERVAL_SECONDS
15
METRICS_MINUTE_RETENTION_DAYS
7
Minute rows before hourly rollup.
METRICS_RETENTION_DAYS
90
Leave the three HOST_* paths unset outside a container. Unset inside one
means every metric describes that container rather than the machine — which
looks entirely plausible on a dashboard and is wrong.
Public address of this control plane. Without it the portal shows webhook paths only, and server enrolment refuses rather than generating a command containing undefined.
The dashboard, the marketing site, and this documentation site each own their own
.env.example. Only VITE_-prefixed variables reach a browser bundle, and none of the three
holds a secret.
App
Key variables
Dashboard
PORT, VITE_APP_TITLE, VITE_API_URL (empty on a self-hosted install, where the dashboard and API share an origin)
VITE_-prefixed values are inlined at build time. Supplying one at
runtime does nothing — they are build arguments to the image, not runtime
environment.