| title | Configuration |
|---|---|
| description | Required and optional environment variables for running Mantis. |
See .env.example in the main Mantis repo for the canonical, commented list.
These are the variables operators usually need to understand.
-
DATABASE_URL— Postgres connection string. Used directly when you run Mantis from source (pnpm dev,pnpm db:migrate) — point it at your own Postgres. The Docker Compose deploy ignores this value and derives its own fromPOSTGRES_PASSWORD(connecting to hostpostgres, notlocalhost), so alocalhostvalue kept here for run-from-source does no harm. -
POSTGRES_PASSWORD— password for the Docker Compose Postgres service, and the single source of truth from which Compose builds the app'sDATABASE_URL. It ships empty in.env.exampleand has no insecure default: both Compose services abort on boot if it is unset or empty. Generate it (together with the pepper below) by running./scripts/setup.sh— a barecp .env.example .envis not enough to start the stack. Not needed when running from source against your own Postgres. -
MANTIS_API_KEY_PEPPER— server-side secret used as the HMAC key when hashing API keys at rest. Generate withopenssl rand -base64 32. A leaked database alone is useless to an attacker without this value too. Do not rotate after the first key is minted — rotating invalidates every existing API key used by the CLI or API clients. If you must rotate, plan to re-mint and re-distribute every API key on the same maintenance window.Upgrading from a pre-pepper deployment? Set the pepper once and existing API keys keep working — the verifier accepts the old pre-pepper SHA-256 hashes too, and opportunistically re-hashes each row to the peppered form on next use. After a few weeks of normal traffic the legacy rows drain to zero.
-
MANTIS_SECRET_KEY(optional) — envelope-encryption key for operator secrets stored in the database: per-destination webhook HMAC signing secrets, the Apple Wallet auth secret, and the.p12certificate passphrase. When set, those columns are sealed at rest with AES-256-GCM (anencv1:envelope) so a database-only leak (backup, read replica) yields no usable secrets — the same trust model as the pepper above. Generate withopenssl rand -base64 32(a 64-character hex string also works; it must decode to exactly 32 bytes). Leave it unset to keep plaintext-at-rest behavior. The feature is backward compatible: existing plaintext rows are read transparently, and once the key is set, new writes are encrypted while legacy rows migrate to ciphertext the next time a signing secret is rotated or the wallet config is re-saved. Don't rotate this key without re-saving those secrets, and don't remove it once rows are encrypted — reads of sealed values will then fail.
PUBLIC_BASE_URL— public origin used when Mantis generates trigger, status, and Wallet callback URLs. Defaults tohttp://localhost:3000.MANTIS_PUBLIC_PATH— trigger path prefix, default/c. Changing this changes generated URLs and the public-only host allowlist; if you choose a different path, put a reverse proxy in front that rewrites it to/c/<id>on the app.
BOOTSTRAP_API_KEY— pre-seed the first admin API key. If unset and no API keys exist, Mantis mints one and prints it on first boot.AUTO_MIGRATE=1— apply pending migrations on app boot. Useful for long-running container deploys; avoid for Vercel/serverless.RUN_NOTIFY_WORKER=0|1— notification worker switch. It runs by default outside Vercel; set0when using cron mode instead.CRON_SECRET— required to enableGET/POST /api/cron/notifications. The endpoint fails closed with 401 when unset.LOG_LEVEL—trace|debug|info|warn|error, defaultinfo.
SMTP_URL— enables email notification destinations. Example:smtp://user:pass@host:587.SMTP_FROM— email sender, defaultMantis <mantis@localhost>.ALLOW_PRIVATE_WEBHOOKS=1— allow webhook/Slack/Discord/Teams targets that resolve to private, loopback, link-local, or cloud-metadata IPs. Default is blocked as an SSRF guard.
Beyond the per-key destinations managed with mantis destinations, an admin can
set instance-wide destinations from the dashboard at /settings/notifications.
Every key's hits fan out to its own destinations plus every global one, so you
can set a Slack or webhook once and have every key you mint — including
bulk-created ones — alert you without further setup. A destination listed both
globally and on a key fires once (the key's own row wins, keeping its signing
secret and activation history). There is no environment variable or CLI
subcommand for the global set; it lives only in the admin dashboard.
TRUST_PROXY_HEADERS=1— trustcf-connecting-ip,x-vercel-forwarded-for,x-real-ip, andx-forwarded-forfor forensic IP logging. Set only behind a proxy that strips and re-injects those headers. Auto-enabled on Vercel and in non-production; setTRUST_PROXY_HEADERS=0to force it off even there. In production with no trusted proxy (the default), client IPs are recorded asnullrather than spoofable values, and Mantis logs a one-time startup warning.TRUSTED_IP_HEADER— pin client-IP extraction to a single header (matched case-insensitively), e.g.x-real-ip. By default Mantis triescf-connecting-ip,x-vercel-forwarded-for,x-real-ip, thenx-forwarded-forand takes the first present; behind a non-Cloudflare proxy (nginx, Caddy, Traefik) that sets onlyx-real-ipand doesn't strip an inboundcf-connecting-ip, an attacker could forgecf-connecting-ipand have it trusted. Pin this to the one header your proxy authoritatively writes so all others are ignored. Only takes effect whenTRUST_PROXY_HEADERSis on.TRUST_PROXY_HOPS— number of trusted reverse-proxy hops in front of Mantis, default1(clamped to 1–16). Only affectsx-forwarded-forparsing: the client IP is taken this many entries from the right of the chain (your nearest proxy appends the real peer on the right), so a client can't forge it past your proxy. Raise it only if you stack multiple trusted proxies; it has no effect on single-value headers likecf-connecting-iporx-real-ip.FORCE_SECURE_COOKIES— override theSecureflag on the dashboard session cookie.1forces it on,0forces it off; unset derives it from the request scheme (X-Forwarded-Proto, or the RFC 7239Forwardedheader's leftmost hop). Set1behind a proxy or tunnel that terminates real TLS but sets neither scheme header — otherwise the session cookie travels un-Secureover genuine HTTPS. Leave it unset for the proxies Mantis documents (Cloudflare, cloudflared, Tailscale, nginx), which all set a scheme header. Don't force it on over plain HTTP: the browser would then never send the cookie back and login would break.PUBLIC_ONLY_HOSTS— comma/space-separated hostnames that should expose only public routes: trigger URLs, status URLs, and Wallet callbacks.DASHBOARD_HOSTS— hostnames allowed to serve the dashboard and management API. WhenPUBLIC_ONLY_HOSTSis set, unknown hosts fail closed to public-only unless explicitly listed here.PUBLIC_ONLY_ALLOW_HEALTH=1— allow/api/healthon public-only hosts.PUBLIC_ONLY_ALLOW_INBOX=1— allow/inboxand/api/inboxon public-only hosts. Use only for deliberate dev boxes.
MANTIS_DUPLICATE_LOG_LIMIT— duplicate hit rows to store per dedupe window after the first hit. Default10;0stores only the first hit in the window.MANTIS_MAX_STORED_REQUEST_FIELD_CHARS— cap for storeduser-agent,referer, and individual header values. Default16384.MANTIS_MAX_STORED_HEADER_SNAPSHOT_CHARS— cap for the total stored header snapshot. Default65536.
Unset retention variables mean retain forever. The notify worker sweeps hourly;
cron mode runs the same sweep through /api/cron/notifications.
MANTIS_HIT_RETENTION_DAYS— delete hits older than N days; notifications cascade.MANTIS_NOTIFICATION_RETENTION_DAYS— delete settled notifications older than N days.MANTIS_AUDIT_RETENTION_DAYS— delete append-only audit events older than N days.MANTIS_SESSION_RETENTION_DAYS— delete revoked or expired dashboard sessions older than N days. Active sessions are not affected.
ENABLE_DEV_INBOX=1— enable the in-memory/inboxwebhook capture and/api/inboxJSON API. The/inbox/<slug>capture endpoint is unauthenticated by design (so it can receive webhooks), but reading or clearing the buffer (GET/DELETE /api/inbox) requires an API key or dashboard session. Leave off in production.
MANTIS_BIND_HOST— compose host bind address, default127.0.0.1.MANTIS_HOST_PORT— compose host port, default3000.TS_AUTHKEY,TS_HOSTNAME,TS_PRIVATE_HOSTNAME,TS_PUBLIC_HOSTNAME,TS_EXTRA_ARGS— Tailscale sidecar configuration.CLOUDFLARE_TUNNEL_TOKEN— Cloudflare Tunnel sidecar token.
Apple Wallet can be configured with env vars or from the admin dashboard at
/settings/wallet. Env vars take precedence over the DB-stored dashboard config.
Required to enable .pkpass generation:
APPLE_PASS_CERT_PATH— Pass Type ID.p12certificate.APPLE_PASS_CERT_PASS— password for the.p12.APPLE_PASS_TEAM_ID— 10-character Apple Team ID.APPLE_PASS_TYPE_ID— Pass Type ID, for examplepass.com.example.mantis.APPLE_PASS_AUTH_SECRET— random secret used to derive per-pass Wallet auth tokens.
Optional:
APPLE_PASS_WWDR_PATH— Apple WWDR intermediate PEM override. Mantis also bundles a WWDR cert.APPLE_PASS_ICON_PATH— custom 58x58 PNG icon.APPLE_PASS_LOGO_PATH— custom 160x50 PNG logo.APPLE_PASS_ORG_NAME— pass organization name, defaultMantis.APPLE_PASS_APNS_KEY_PATH— APNs.p8auth key for pass-update pushes.APPLE_PASS_APNS_KEY_ID— APNs key ID.APPLE_PASS_APNS_SANDBOX=1— use APNs sandbox instead of production.