Configuration
The server loads its configuration in this order of precedence:
defaults < YAML file < environment. Point it at a file with --config (or
NAUTHERA_SERVER_CONFIG). Environment variables always win.
nauthera-server --config /etc/nauthera/config.yamlThe annotated reference is config.example.yaml in the nauthera-server repository.
Environment variables
Every key has the prefix NAUTHERA_SERVER_. To get the variable name, upper-case the
dotted key and replace . and - with _:
server.http.read_header_timeout -> NAUTHERA_SERVER_SERVER_HTTP_READ_HEADER_TIMEOUT
database.dsn -> NAUTHERA_SERVER_DATABASE_DSN
tracing.sample_ratio -> NAUTHERA_SERVER_TRACING_SAMPLE_RATIODurations are Go duration strings ("15s", "30m"). Lists are comma-separated.
Prefer the file. The file is decoded strictly, so a mistyped key is a startup error that names it. A mistyped environment variable is silently ignored, and you run on a default you believe you overrode. The Helm chart renders
configto a file for this reason.
Secrets from files
Keys that hold secrets also accept a mounted file. Append _FILE to the variable name:
NAUTHERA_SERVER_DATABASE_DSN_FILE=/var/run/secrets/db/uri
NAUTHERA_SERVER_DATABASE_PASSWORD_FILE=/var/run/secrets/db/password
NAUTHERA_SERVER_CACHE_PASSWORD_FILE=/var/run/secrets/redis/password
NAUTHERA_SERVER_MFA_ENCRYPTION_KEY_FILE=/var/run/secrets/mfa/key
NAUTHERA_SERVER_KEYS_ENCRYPTION_KEY_FILE=/var/run/secrets/keys/kekThe retired-key lists (mfa.retired_keys, keys.retired_keys) have no file form and are
read from the environment.
Top-level sections
| Section | What it configures |
|---|---|
server | The three listeners: server.http (:8080, timeouts, body cap, TLS), server.admin (:9090, pprof), server.grpc (:9091, mTLS, allowed_client_identities, allow_insecure), plus shutdown_timeout and drain_delay. |
logging | level (debug, info, warn or error), format (json or console), development. |
database | driver (postgres or sqlite), a dsn or discrete host, port, user, password, name and sslmode, pgbouncer, pool sizing, and migrate. |
cache | A Redis-compatible cache: enabled, address, credentials, tls, timeouts and pool size. Required for any issuer that is not loopback. |
metrics | enabled, path (default /metrics on the admin listener). |
rate_limit | A token bucket: rps, burst, backend (memory or redis), window. |
security | trusted_proxies, hsts, frame_deny, content_security_policy, cors.*. |
issuer | url and the token lifetimes: access_token_ttl (10m), id_token_ttl (10m), refresh_token_ttl (720h), auth_code_ttl (60s). |
keys | Signing keys: provider (memory for development, or file), signing_key_path, rsa_signing_key_path, verification_key_paths and rsa_verification_key_paths for retired public keys, default_alg, plus the per-organization key-encryption key encryption_key and its retired_keys. |
tracing | OpenTelemetry over OTLP/gRPC: enabled, endpoint, insecure, sample_ratio, environment. |
auth | brute_force.* (lockout and per-IP and per-identifier throttles), session.* (cookie name, idle and absolute TTL), session_management.enabled, registration.enabled, admin.subjects and admin.bootstrap_secret_path. |
mfa | required, provider (memory or file), encryption_key, retired_keys, issuer (the label shown in authenticator apps), drift, max_failures, lock_duration, recovery_code_count. |
orgs | default_slug, the main organization's slug (default). |
audit | enabled, level, include and exclude globs, fail_closed, buffering, retention (9600h), partition_ahead_months. See Audit Log. |
fapi | require_pushed_authorization_requests, which requires PAR deployment-wide. |
fips | required, which refuses to start unless the Go FIPS 140-3 module is active (a GOFIPS140=v1.0.0 build run with GODEBUG=fips140=on). |
Production guards
When the issuer host is not loopback, the server refuses to start unless:
- the issuer is
httpswith no path, cache.enabledistrue(otherwise authorization codes and refresh tokens would live in one replica's memory),keys.providerisfileandmfa.providerisfile(otherwise keys would be regenerated on every restart), andsecurity.trusted_proxiesis not empty.
localhost and names under .localhost count as loopback, so local development gets a
real HTTPS issuer without having to run Redis.
Example: minimal production file
issuer:
url: https://auth.example.com
database:
driver: postgres
host: postgres.example.svc
user: nauthera
name: nauthera
sslmode: verify-full
migrate: true
cache:
enabled: true
address: redis.example.svc:6379
tls: true
keys:
provider: file
signing_key_path: /etc/nauthera/keys/ec/tls.key
rsa_signing_key_path: /etc/nauthera/keys/rsa/tls.key
mfa:
provider: file
security:
trusted_proxies: ["10.0.0.0/8"]
hsts: true
server:
grpc:
tls:
enabled: true
cert_file: /etc/nauthera/grpc/tls.crt
key_file: /etc/nauthera/grpc/tls.key
client_ca_file: /etc/nauthera/grpc/ca.crt
allowed_client_identities: ["nauthera-operator"]Supply the database password, the MFA encryption key and the key-encryption key through
the _FILE variables above.