Skip to content

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 ​

VariableDefaultDescription
JARVIS_PORT8080HTTP listen port
JARVIS_LOG_LEVELinfoLog verbosity: info or debug
JARVIS_LOG_REQUESTSfalseLog every HTTP request. Noisy — for debugging a proxy or auth problem, not for normal operation
JARVIS_POLL_INTERVAL15sHow 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_TTL20mHow 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 to poll_snapshots on 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.

VariableDefaultDescription
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.

VariableDescription
JARVIS_CLUSTER_1_OAUTH2_CLIENT_IDOAuth2 client ID (client_credentials grant, tokens refreshed automatically)
JARVIS_CLUSTER_1_OAUTH2_CLIENT_SECRETOAuth2 client secret — never logged. Required with OAUTH2_CLIENT_ID
JARVIS_CLUSTER_1_OAUTH2_TOKEN_URLToken endpoint. Required with OAUTH2_CLIENT_ID
JARVIS_CLUSTER_1_OAUTH2_SCOPESComma-separated scopes
JARVIS_CLUSTER_1_BEARER_TOKENStatic token, sent as Authorization: Bearer <token>
JARVIS_CLUSTER_1_BASIC_AUTH_USERHTTP basic auth user
JARVIS_CLUSTER_1_BASIC_AUTH_PASSWORDHTTP basic auth password — never logged
JARVIS_CLUSTER_1_HEADER_<name>Custom request header, repeat for several

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 ​

VariableDefaultDescription
JARVIS_DB_DSN/data/jarvis.dbSelects the backend and the connection: a file path means SQLite, a postgres:// URL means PostgreSQL. Never logged in full — the password is redacted
JARVIS_DB_MAX_OPEN_CONNS10PostgreSQL connection pool cap, at least 2 (a configured 1 is raised to 2 with a warning in the log). Ignored on SQLite, which is deliberately limited to a single writer
env
JARVIS_DB_DSN=/data/jarvis.db
# or
JARVIS_DB_DSN=postgres://jarvis:secret@postgres:5432/jarvis?sslmode=require

SQLite 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 ​

VariableDefaultDescription
JARVIS_AUTH_PROVIDERnonenone, 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_MODEwrite_protectApplies 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_CONNECTIONS500Cap 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_SECUREautoauto 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_SCOPESopenid,profile,emailComma-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
env
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.

VariableDefaultDescription
JARVIS_SILENCE_DURATIONS5m,10m,15m,30m,1h,4h,1d,1wSilence durations, comma-separated
env
JARVIS_SILENCE_DURATIONS=15m,1h,4h,1d,1w,30d

Each 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.

VariableDefaultDescription
JARVIS_RETENTION_DAYS—Fallback age limit for every history type. 0 or unset disables the sweep entirely
JARVIS_RETENTION_EVENTS_DAYS(inherits)Alert lifecycle events
JARVIS_RETENTION_CLAIMS_DAYS(inherits)Released claims
JARVIS_RETENTION_SILENCE_EVENTS_DAYS(inherits)Silence events
JARVIS_RETENTION_COMMENTS_DAYS0 (kept forever)Comments — never inherits JARVIS_RETENTION_DAYS; only an explicit value here enables deletion
JARVIS_RETENTION_SWEEP_INTERVAL12hHow often the sweep runs

What exactly gets deleted, in which order, and how this affects per-cluster statistics is described in Data retention.

Released under the Apache 2.0 License. Jarvis is not affiliated with Prometheus or Alertmanager.