Configuration
Every variable is prefixed RUNTIME_, so env | grep RUNTIME_ is a complete
answer. There is no configuration file: the environment is read once, at
start-up, in the binary.
runtime config prints what actually took effect and exits — because “which
value won” is a question asked at the worst possible moment.
Every variable
| Variable | Default | Notes |
|---|---|---|
RUNTIME_DATABASE_URL | — | Required to serve |
RUNTIME_APP_ADDR | 0.0.0.0:8080 | End-user traffic |
RUNTIME_ADMIN_ADDR | 127.0.0.1:9090 | Widen in a container; never route to it |
RUNTIME_DATABASE_MAX_CONNECTIONS | 10 | See below |
RUNTIME_DRAIN_DELAY | 5 | Seconds. See below |
RUNTIME_OIDC_ISSUER | — | Both or neither |
RUNTIME_OIDC_AUDIENCE | — | Both or neither |
RUNTIME_OTLP_ENDPOINT | — | Unset exports to stdout |
RUNTIME_ENVIRONMENT | development | Tags every span, metric and log |
RUNTIME_LOG | info | tracing filter directives |
RUNTIME_LOG_FORMAT | json | Or pretty, for a laptop |
The app and admin addresses must differ, and the loader refuses to start otherwise.
The pool size is the capacity knob
RUNTIME_DATABASE_MAX_CONNECTIONS is per process. The number to reason about is
not this one but this one times the replica count, against PostgreSQL’s own
max_connections. A value that looks modest per pod is how a scale-up takes the
database down.
Requests that arrive with the pool saturated wait five seconds for a connection and then fail. That is deliberately shorter than the thirty-second request timeout: a brief slowdown should produce prompt errors that readiness and your metrics both show, not a pile of requests all waiting out the full timeout and outliving the slowdown that caused them.
Authentication is both or neither
RUNTIME_OIDC_ISSUER=https://login.example.com/realms/acme
RUNTIME_OIDC_AUDIENCE=runtime
Set one without the other and the runtime refuses to start. An issuer with no audience accepts tokens minted for every other service that provider serves — a different tenant, a different application — and it does so silently.
Keys are discovered from the issuer’s /.well-known/openid-configuration at
start-up, so a misconfigured issuer stops a deploy rather than becoming a 500
on whoever calls first.
With neither set, no bearer token can be verified: every authenticated
request is a 401 and only the model’s anonymousRole reaches anything. If the
model declares no anonymous role either, the runtime refuses to start — a process
that could serve nobody is the most expensive kind of healthy.
What the runtime never reads
There is no configuration file, no .env loading, and no remote configuration
service. The .env.example in the runtime repository is the documented variable
list, not a mechanism — nothing loads it.