Configuration
Jarvis is configured entirely through environment variables. There is no config file. .env.example in the repository is a working template of everything on this page.
Values are read once at startup — a change means a restart.
- Core
- Clusters — the only required settings
- Upstream authentication
- Database
- User authentication
- Silence durations
- Data retention
Core
| Variable | Default | Description |
|---|---|---|
JARVIS_PORT | 8080 | HTTP listen port |
JARVIS_LOG_LEVEL | info | Log verbosity: info or debug |
JARVIS_LOG_REQUESTS | false | Log every HTTP request. Noisy — for debugging a proxy or auth problem, not for normal operation |
JARVIS_POLL_INTERVAL | 15s | How often Alertmanager is polled (Go duration, e.g. 30s). Also scales the grace period to max(60s, 2 × interval) — see Alert lifecycle |
JARVIS_RESOLVED_BUFFER_TTL | 20m | How long a resolved alert stays in the live snapshot, which feeds the Recently resolved toggle on the Active tab (Go duration, 1m to 24h; anything else stops the start). Instance-wide — set the same value on every replica. Memory and performance: every resolved alert inside the window stays in memory and is part of each WebSocket push, each GET /api/v1/alerts response and, on PostgreSQL, each poll snapshot row, so the cost grows with the number of distinct alerts that resolve within the window (a flapping alert occupies one slot). The UI shows the active value in the tooltips of the Recently resolved toggle and the Resolved label (GET /api/v1/status → resolved_buffer_ttl_seconds). Values above 1h log a warning at startup. The value may be shorter than the grace period; that is harmless. See Resolved-buffer window |
JARVIS_ALLOWED_ORIGINS | (same origin) | Comma-separated origins allowed for CORS, the WebSocket upgrade and state-changing requests (POST/PUT/PATCH/DELETE). The server's own host is always accepted too, for the WebSocket and writes alike, e.g. https://jarvis.example.com. Required whenever the browser reaches Jarvis under a different host than the backend itself — see Running behind a proxy. No wildcard is accepted. With JARVIS_AUTH_PROVIDER=internal or oidc and no value set, Jarvis logs a warning at startup |
JARVIS_RUNBOOK_BASE_URL | — | Prefix for runbook links. Prepended to the runbook label or annotation when its value is not already an absolute URL, e.g. https://wiki.example.com/runbooks/ |
JARVIS_PPROF_ADDR | — | Opt-in loopback-only pprof debug server (heap/allocs/goroutine profiles only), e.g. 127.0.0.1:6060. Empty (default) opens no port at all. Must be a literal loopback IP + port — see Troubleshooting |
Resolved-buffer window
JARVIS_RESOLVED_BUFFER_TTL decides how long an alert that disappeared from Alertmanager still travels with the live snapshot. It does not change what is recorded: the full history stays in the database and in the Resolved view. The live snapshot is what every client receives on connect and on every change, so a longer window is a real cost:
- Memory: one entry per distinct alert (fingerprint and cluster) that resolved inside the window. Repeated flapping of the same alert overwrites its entry instead of adding one.
- Payload: each entry adds to every WebSocket push and every unfiltered
GET /api/v1/alerts. On PostgreSQL it is also written topoll_snapshotson each poll and decoded by every follower. - Start: the buffer is re-seeded from the database with the same window, so a restart shows the same recently resolved alerts.
Start with the default and raise it only when the resolved-alert volume is small compared with the number of active alerts. Watch jarvis_alerts{state="resolved"} and the process memory after changing it.
Clusters
At least one cluster is required. Repeat the variables with _2_, _3_, … for more clusters. The numbering must be contiguous. Setup examples, multi-cluster behavior, Alertmanager HA deduplication, member aliases, and silence writes are covered in Connect Alertmanager.
| Variable | Default | Description |
|---|---|---|
JARVIS_CLUSTER_1_NAME | — | Display name, shown in the UI and used in metrics (required) |
JARVIS_CLUSTER_1_ALERTMANAGER_URL | — | Alertmanager URL as Jarvis reaches it (required). A comma-separated list makes this one HA cluster — see Connect Alertmanager |
JARVIS_CLUSTER_1_PROMETHEUS_URL | — | Prometheus URL, used for the "source" links on an alert |
JARVIS_CLUSTER_1_HOST_ALIAS | — | The Alertmanager URL as the browser reaches it, when that differs from the internal one. One value applies to every member; a comma-separated list matching the member count sets one alias per member, in the same order |
Upstream authentication
For an Alertmanager behind an authentication proxy. All of these are per cluster and apply to every member alike.
Priority when more than one is set: OAuth2 → bearer token → basic auth → custom headers. OAuth2 is the recommended option because the token is refreshed for you. Provider setup, including a worked Keycloak example and the Helm chart's clusters[].auth values, is in Alertmanager authentication.
Database
JARVIS_DB_DSN=/data/jarvis.db
# or
JARVIS_DB_DSN=postgres://jarvis:secret@postgres:5432/jarvis?sslmode=requireSQLite is the default and is meant for evaluation, homelabs and any single-replica deployment — it needs no setup at all. Use PostgreSQL for production, for high availability and whenever you want more than one replica.
Schema, migrations, TLS, multi-replica leader election and failover, and Kubernetes deployment including a CloudNativePG example are covered in PostgreSQL & HA.
User authentication
| Variable | Default | Description |
|---|---|---|
JARVIS_AUTH_PROVIDER | none | none, internal or oidc. With none nothing requires a login — anyone who can reach Jarvis can read alerts and create claims, comments and silences — and Jarvis logs a warning at startup |
JARVIS_AUTH_MODE | write_protect | Applies when the provider is not none. write_protect lets anyone read and requires a login to change anything; full_protect requires a login for everything |
JARVIS_SECRET_KEY | — | Signing key for session tokens, at least 32 bytes. Required for internal and oidc |
JARVIS_ALLOWED_HOSTS | (off) | Optional Host-header allow-list, comma-separated host or host:port (a host without port matches any port, case-insensitive), e.g. jarvis.example.com. A request with any other Host gets 421 Misdirected Request. /health, /health/live, /health/ready and /metrics are exempt, so probes and scrapes by pod IP keep working. List every name users reach Jarvis under, or they are locked out |
JARVIS_TRUSTED_PROXIES | (none) | Comma-separated CIDRs or plain IPs of the reverse proxies in front of Jarvis. Only a request arriving from one of them has its X-Forwarded-For believed when deriving the client IP (remote_ip in the logs); the right-most address not belonging to a listed proxy wins. Empty: the direct TCP peer is always used and X-Forwarded-For / X-Real-IP are ignored. Private ranges are not trusted implicitly. An invalid entry stops startup |
JARVIS_WS_MAX_CONNECTIONS | 500 | Cap on simultaneous WebSocket connections per pod. One over the cap is refused with 503 and Retry-After before the upgrade and counted in jarvis_ws_rejected_total; the browser reconnects on its own. 0 removes the cap. There is no per-IP limit: Jarvis is an internal tool, and behind a proxy the peer address is the proxy's anyway |
JARVIS_METRICS_TOKEN | (open) | Optional bearer token for GET /metrics. Set, a scrape without Authorization: Bearer <token> gets 401; unset, the endpoint stays open as before. Other routes and /health* are unaffected. See Monitoring and metrics |
JARVIS_COOKIE_SECURE | auto | auto sets the Secure flag on the session cookie when the request is HTTPS, directly or via X-Forwarded-Proto: https. true always sets it, for a TLS-terminating proxy that does not send that header (without it a browser on https:// still receives a non-Secure cookie). Anything else stops the start |
JARVIS_SETUP_TOKEN | — | Optional. With internal auth, creating the first admin through /setup then requires this token, so nobody else can claim a fresh instance before you do. The setup page asks for it. Unset keeps the wizard open. Ignored (with a startup warning) for other providers |
JARVIS_AUTH_OIDC_ISSUER | — | Issuer URL (required for oidc) |
JARVIS_AUTH_OIDC_CLIENT_ID | — | Client ID (required for oidc) |
JARVIS_AUTH_OIDC_CLIENT_SECRET | — | Client secret (required for oidc) |
JARVIS_AUTH_OIDC_REDIRECT_URL | — | Callback URL, must match the provider's configuration (required for oidc) |
JARVIS_AUTH_OIDC_SCOPES | openid,profile,email | Comma-separated scopes |
JARVIS_OIDC_GROUPS_CLAIM | — | ID-token claim that carries the user's groups, e.g. groups or cognito:groups. Jarvis stores them at each login and shows them in the Account panel of the user menu. Without it groups are not read |
JARVIS_OIDC_ADMIN_VALUE | — | The group in that claim that makes a user an admin, e.g. jarvis-admins. Several groups: comma-separated (admin_a,admin_b), membership in any one is enough; whitespace around entries is ignored. Group names that themselves contain a comma (e.g. full LDAP DNs) cannot be listed this way. Needs JARVIS_OIDC_GROUPS_CLAIM; without both every OIDC user gets the user role |
JARVIS_AUTH_PROVIDER=internal
JARVIS_SECRET_KEY=<openssl rand -hex 32>With internal, the first visit redirects to /setup to create the admin account (set JARVIS_SETUP_TOKEN to require a token there); further users are managed under Administration in the user menu. Provider setup for Keycloak and Authentik, the OIDC flow, role mapping, session details and Kubernetes secrets are in User authentication.
Silence durations
The durations offered by the one-click Fast-Silence menu and the Extend silence menu are configurable — one list serves both. Set an instance-wide default here; every user can still change their own list in Settings → Silences — that list wins over this one. Without either, the built-in list applies.
| Variable | Default | Description |
|---|---|---|
JARVIS_SILENCE_DURATIONS | 5m,10m,15m,30m,1h,4h,1d,1w | Silence durations, comma-separated |
JARVIS_SILENCE_DURATIONS=15m,1h,4h,1d,1w,30dEach entry is a number plus a unit: m minutes, h hours, d days, w weeks, y years (365 days) — for example 30m, 4h, 1d, 1w, 30d, 1y. Every value must be between 1m and 365d, and a list holds at most 12 entries; order and duplicates are sorted out for you. An invalid value stops Jarvis from starting, with the variable named in the error. What the menus and the per-user override look like is described in Fast-Silence.
Data retention
Off by default: Jarvis keeps history forever unless you tell it otherwise.
What exactly gets deleted, in which order, and how this affects per-cluster statistics is described in Data retention.
