Repository navigation
Configuration
Marmot is configured through environment variables. Every variable is declared and validated in
src/env.ts (zod) when a process starts, so a typo or a missing required value fails fast with a readable
message instead of a half-working install. .env.example at the repository root lists all of them for local
development; docker/.env.example is the shorter compose variant.
Three processes read the environment: web (Next.js + Payload: UI, API, status pages), worker
(checks, heartbeats, notifications, retention) and realtime (socket.io). With MARMOT_ROLE=all one
container runs all three. The Read by column below tells you which process must see a change; in a
compose stack the .env file is passed to all three, so you can simply restart the stack.
Boolean variables accept 1, true, yes, on (case-insensitive) as true and anything else as false.
| Variable | Default | Read by | Description |
|---|---|---|---|
PAYLOAD_SECRET |
— (required) | all | Signs session tokens and encrypts secrets at rest. At least 16 characters; use openssl rand -hex 32. Changing it signs everyone out. |
NEXT_PUBLIC_SERVER_URL |
http://localhost:3000 |
all | The URL users open Marmot at. Used for CORS/CSRF, invitation and password-reset emails, the OIDC redirect URI, status-page links, custom-domain detection and the realtime CORS origin. Must match the public https:// URL behind a proxy. |
ADDITIONAL_ORIGINS |
(empty) | web | Extra CORS/CSRF origins, comma-separated (e.g. a second hostname behind the same proxy). |
MARMOT_ROLE |
all |
all | Which process this container runs: web, worker, realtime or all. The compose file sets it per service. |
NODE_ENV |
development |
all |
development, test or production. The Docker image sets production, which enables HSTS, refuses plain-http:// OIDC issuers and makes the database adapters require migrations instead of pushing the schema. |
LOG_LEVEL |
info |
all | pino level: fatal, error, warn, info, debug, trace. Logs are JSON lines on stdout. |
| Variable | Default | Read by | Description |
|---|---|---|---|
DATABASE_ADAPTER |
postgres |
all |
postgres, mongodb or sqlite. SQLite is for local development only (no migrations, pushes its schema). |
DATABASE_URL |
— (required) | all | Connection string for the adapter: postgres://user:pass@host:5432/marmot, mongodb://host:27017/marmot, file:./data/marmot.db. Inside compose the host is the service name (postgres, mongo). |
Postgres migrations live in src/migrations/postgres and run when the web role starts (or with the image's
migrate command). MongoDB needs no schema migrations; indexes are created on boot.
| Variable | Default | Read by | Description |
|---|---|---|---|
REDIS_URL |
redis://localhost:6379 |
all | BullMQ queues and job schedulers (worker, and the web process when monitors change) plus socket.io pub/sub (realtime, and the emitters in web and worker). Run Redis with maxmemory-policy noeviction. |
Redis holds only queue state and live socket rooms. It does not need a backup: the worker re-creates every job scheduler from the database on start.
| Variable | Default | Read by | Description |
|---|---|---|---|
REALTIME_PORT |
3001 |
realtime | Port the socket.io server listens on. GET /healthz on the same port is the health probe. |
NEXT_PUBLIC_REALTIME_URL |
(empty) | web (build time) | Origin of the socket server when it is not reachable at /socket.io on the web origin. Leave empty with the bundled Caddy or any proxy that routes /socket.io/* to the realtime process. Inlined into the browser bundle at pnpm build; the published image has it empty. |
See Realtime origin below.
Uploads (organization logos, status-page logos) go to local disk unless S3_BUCKET is set, in which case
@payloadcms/storage-s3 is registered and the other S3_* variables configure the client.
| Variable | Default | Read by | Description |
|---|---|---|---|
UPLOADS_DIR |
uploads |
web | Local upload directory, relative to the working directory. The image uses /app/uploads (a volume). |
S3_BUCKET |
— | web | Bucket name. Setting it switches storage to S3. |
S3_REGION |
— | web | Region (auto for Cloudflare R2 and most S3-compatible stores). |
S3_ENDPOINT |
— | web | Custom endpoint for S3-compatible services (R2, MinIO, …). Leave unset for AWS. |
S3_ACCESS_KEY_ID |
— | web | Access key. |
S3_SECRET_ACCESS_KEY |
— | web | Secret key. |
S3_FORCE_PATH_STYLE |
false |
web | Use path-style URLs (https://endpoint/bucket/key), needed by MinIO and some proxies. |
Marmot sends invitations and password-reset mail through the Payload email adapter. Without SMTP_HOST the
messages are written to the web process log instead of being sent, which is enough to copy an invitation
link during evaluation. The smtp notification provider can reuse these settings (Use the server SMTP
settings); the last two variables decide who may do that and how much mail it may send.
| Variable | Default | Read by | Description |
|---|---|---|---|
SMTP_HOST |
— | web, worker | SMTP server. Unset = log mail to the console. |
SMTP_PORT |
587 |
web, worker | SMTP port. |
SMTP_USER |
— | web, worker | Username (optional for unauthenticated relays). |
SMTP_PASSWORD |
— | web, worker | Password. |
SMTP_SECURE |
false |
web, worker |
true for implicit TLS (usually port 465); false uses STARTTLS when offered. |
EMAIL_FROM |
Marmot <marmot@localhost> |
web, worker | Sender address, Name <address> form allowed. |
NOTIFICATIONS_SERVER_SMTP |
superadmin |
web, worker | Who may set up an smtp notification channel that sends through the settings above: all (anyone who manages channels), superadmin (instance superadmins only) or off (nobody; such channels fail to deliver). |
NOTIFICATIONS_SERVER_SMTP_RATE |
60 |
web, worker | Messages per organization per hour sent through the server SMTP settings, test messages included. 0 = unlimited. |
Notification mail sent through the server settings is limited to 10 recipients per message (to, cc and bcc
combined). In superadmin mode, channels set up before the upgrade keep sending; see
Deployment for how to list them. Details: Notifications.
| Variable | Default | Read by | Description |
|---|---|---|---|
DISABLE_SIGNUP |
false |
web | Default for the allowSignup instance setting. When sign-up is off, accounts are created only through the setup wizard, invitations, or SSO logins that match a pending invitation. |
OIDC_ISSUER_URL |
— | web | Issuer of your OpenID Connect provider (its iss value). Discovery is read from <issuer>/.well-known/openid-configuration. |
OIDC_CLIENT_ID |
— | web | Client id registered at the provider. |
OIDC_CLIENT_SECRET |
— | web | Client secret. All three OIDC_ISSUER_URL, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET must be set to enable the SSO button. |
OIDC_DISPLAY_NAME |
Single sign-on |
web | Label of the login button ("Continue with …"). |
OIDC_AUTO_PROVISION |
true |
web | Create a Marmot account on first SSO login. false only lets existing users (matched by subject or verified email) in. |
OIDC_SCOPES |
openid email profile |
web | Scopes requested from the provider. |
GITHUB_CLIENT_ID |
— | web | GitHub OAuth app client id. With GITHUB_CLIENT_SECRET, enables "Continue with GitHub"; callback URL <NEXT_PUBLIC_SERVER_URL>/api/auth/sso/github/callback. |
GITHUB_CLIENT_SECRET |
— | web | GitHub OAuth app client secret. |
GOOGLE_CLIENT_ID |
— | web | Google OAuth client id. With GOOGLE_CLIENT_SECRET, enables "Continue with Google"; redirect URI <NEXT_PUBLIC_SERVER_URL>/api/auth/sso/google/callback. |
GOOGLE_CLIENT_SECRET |
— | web | Google OAuth client secret. |
Full setup guide with provider walkthroughs: Single sign-on.
| Variable | Default | Read by | Description |
|---|---|---|---|
KEEP_DATA_PERIOD_DAYS |
365 |
web, worker | Default for the keepDataPeriodDays instance setting: how long daily aggregates and important heartbeats are kept. Raw heartbeats live 24 h, minutely buckets 24 h, hourly 30 d. |
WORKER_CONCURRENCY |
10 |
worker | Parallel checks (and notification deliveries) per worker process. Scale out with more worker replicas rather than very high values. |
DOCKER_SOCKET_ENABLED |
true |
web, worker | Allow socket Docker hosts, which talk to the Docker daemon of the worker's own host. Set false on shared installs where users must not reach the local daemon. |
MARMOT_DISABLE_ENGINE_HOOKS |
false |
web | Skip the BullMQ scheduler sync in the monitors collection hooks. Only for tests that run without Redis; the Vitest setup sets it. |
Switches for running Marmot as a hosted service. Self-hosted installs leave them off.
| Variable | Default | Read by | Description |
|---|---|---|---|
LANDING_PAGE_ENABLED |
false |
web | Show a marketing landing page at / to signed-out visitors instead of redirecting to /login. Signed-in users and fresh installs (setup wizard) are unaffected. |
A scaffold for hosted offerings. Self-hosted installs leave it off; every limit is then unlimited and no Stripe code is loaded. Details in Billing (landing in the current release).
| Variable | Default | Read by | Description |
|---|---|---|---|
BILLING_ENABLED |
false |
web | Enforce plan limits and show the Billing settings tab. |
STRIPE_SECRET_KEY |
— | web | Registers the Stripe plugin and enables Checkout / Billing Portal. |
STRIPE_WEBHOOK_SECRET |
— | web | Signing secret of the Stripe webhook endpoint. |
STRIPE_PUBLISHABLE_KEY |
— | web | Reserved for a future Stripe Elements checkout; not needed today. |
Marmot sends nothing by default: no analytics SDK is loaded, no cookie banner appears and nothing calls home. Setting a PostHog key enables opt-in product analytics behind a consent banner; what is collected is documented in Telemetry (landing in the current release).
| Variable | Default | Read by | Description |
|---|---|---|---|
NEXT_PUBLIC_POSTHOG_KEY |
— | web (build time), worker | PostHog project key. Unset = telemetry off. |
NEXT_PUBLIC_POSTHOG_HOST |
https://us.i.posthog.com |
web (build time), worker | PostHog ingestion host (https://eu.i.posthog.com for the EU cloud). Also the target of the /ph rewrite. |
These are read by docker/entrypoint.sh, docker/docker-compose.yml or the Caddyfile rather than by
src/env.ts, so they only matter for Docker deployments.
| Variable | Default | Used by | Description |
|---|---|---|---|
DOMAIN |
— | compose → Caddy | Hostname for automatic HTTPS. Unset = Caddy serves plain HTTP on port 80 (for use behind your own proxy). |
ACME_EMAIL |
— | compose → Caddy | Contact address passed to the certificate authority. |
SITE_ADDRESS |
:80 |
Caddyfile | Derived from DOMAIN by compose; set it directly when running Caddy by hand. |
POSTGRES_PASSWORD |
marmot |
compose | Password of the bundled Postgres; the default DATABASE_URL picks it up. |
MARMOT_VERSION |
release | compose | Image tag of ghcr.io/thinkhumandotdev/marmot to run; a release's compose file defaults to that release. |
PORT |
3000 |
entrypoint (web) | Port of the Next.js server inside the container. |
SKIP_MIGRATIONS |
false |
entrypoint (web) |
true skips migrate on start, for when you run migrations yourself (init container, CI/CD step). |
WORKER_SCHEMA_WAIT_MS |
120000 |
worker | How long the worker waits for the database schema (migrations running in web) before giving up on boot. |
Some options can be changed at runtime by a superadmin without redeploying: in the Marmot UI under
Settings → Instance (/{orgSlug}/settings/instance, visible to superadmins only, saved through
POST /api/globals/instance-settings) or in the Payload admin panel (System → Instance settings, the
instance-settings global). Environment variables provide the defaults; once a value has been saved in the
global it takes precedence over the variable. The Instance tab also shows whether SMTP is configured and
offers Send test email (POST /api/instance/smtp-test { to? }, superadmin-only; answers 400 while
SMTP_HOST is unset).
| Setting | Default | Description |
|---|---|---|
primaryBaseUrl |
NEXT_PUBLIC_SERVER_URL |
Public URL used in notifications and status page links. |
allowSignup |
!DISABLE_SIGNUP |
Whether anyone may create an account (POST /api/users, /signup). |
entryPage |
dashboard |
dashboard or status-page: what the root URL shows. |
tlsExpiryNotifyDays |
7, 14, 21 |
Days before a TLS certificate expires at which to notify. |
domainExpiryNotifyDays |
7, 14, 21 |
Days before a domain registration expires at which to notify. |
keepDataPeriodDays |
KEEP_DATA_PERIOD_DAYS |
Retention of daily aggregates and important heartbeats (0 disables). |
trustProxy |
false |
Trust X-Forwarded-* headers from the reverse proxy for client IPs. |
steamApiKey, globalpingApiToken
|
— | Third-party API keys for the corresponding monitor types (superadmin-only read). |
Server code reads the resolved values through getInstanceSettings(payload) (src/server/settings.ts),
which caches them for 60 seconds per process; saving the global clears the cache of the web process
immediately, the worker and realtime processes pick changes up within a minute.
A fresh install has no users. While that is the case GET /api/setup/status returns { "needsSetup": true },
/ and the auth pages redirect to /setup, and the wizard creates the first superadmin plus their
organization in one transaction (POST /api/setup) and signs them in. Once any user exists the wizard is
closed for good (/setup redirects to /login, the API answers 409); further users join through
/signup (if allowSignup) or invitations.
The browser opens one socket.io connection with credentials, so the realtime server must be reachable on a
URL that receives the payload-token cookie. By default the client connects to the page's own origin at
/socket.io, and Caddy (or your reverse proxy, see Deployment) forwards that path to the
realtime process on REALTIME_PORT. Set NEXT_PUBLIC_REALTIME_URL (e.g. https://realtime.example.com)
only when the realtime server is exposed on another origin; it must share the cookie's site (a subdomain of
the web app is fine) and allows NEXT_PUBLIC_SERVER_URL in CORS. Being a NEXT_PUBLIC_* variable it is
inlined at pnpm build time, so it requires building your own image; the published image expects the
same-origin layout.
This wiki is published from docs/ on every push to main; edits made in the wiki are overwritten.
Found a mistake? Open a pull request with the docs(docs): … prefix.
Run it
- Getting started
- Configuration
- Deployment
- Kubernetes
- Single sign-on
- Security
- Telemetry
- Billing
- Demo mode
Use it
- Monitors
- Monitor types
- Probe locations
- Notifications
- Status pages
- Maintenance
- Organizations and members
- Integrations
- MCP server for AI agents
- Import and export
- CLI and monitors as code
- GitHub Action
Change it