Documentation
On this page

Configuration

Every environment variable the server reads, with its default.

The server reads its configuration from the environment at startup and refuses to start if a required variable is missing or malformed. The Docker image sets some of these to other defaults, and fills in the database and the secret key.

Database

VariableDefaultMeaning
DATABASE_URLrequired if migratingSchema owner. Used only to run migrations at startup.
RUN_MIGRATIONStruefalse skips migrations and makes DATABASE_URL optional.
ORG_DATABASE_URLrequiredThe organization API’s database. Connects as happy_enterprise_org.
MANAGEMENT_DATABASE_URLrequiredConnects as happy_enterprise_management.

Listeners and URLs

VariableDefaultMeaning
ORG_LISTEN_ADDR0.0.0.0:8080Organization API listener.
MANAGEMENT_LISTEN_ADDR127.0.0.1:9090Management API listener. Keep it off the public network.
PUBLIC_BASE_URLhttp://localhost:8080Public origin of the organization API (no path). OAuth issuers and MCP resource URLs derive from it.
INVITE_URL<PUBLIC_BASE_URL>/inviteWhere invite links point (?org=<id>&token=<token> is appended).
CLIENT_IP_HEADERnoneHeader with the client IP set by your reverse proxy (e.g. x-real-ip, or x-forwarded-for: the last entry, so the proxy must not append to it), for rate limits and password lockouts. Unset: the TCP peer. Only set it if the proxy overwrites the header.
MCP_ALLOWED_HOSTShost of PUBLIC_BASE_URLComma-separated Host values (host or host:port) the MCP endpoint accepts, against DNS rebinding; * accepts any.
WEB_DIRnoneDirectory of the built web app, served on the organization listener for every path no API route takes (index.html for the app’s own paths).
MANAGEMENT_MOUNTnoneA path such as /mgmt: also serve the management API under it on the organization listener, for the operator console. Leave it unset to keep the management API off the public network.

Keys

VariableDefaultMeaning
SERVER_SECRET_KEYrequired32 random bytes, base64url without padding. Derives session signing keys and seals TOTP secrets: rotating it logs everyone out and invalidates TOTP factors.
MANAGEMENT_JWT_PUBLIC_KEYSrequired with jwtkid=<base64url raw 32-byte Ed25519 key>[,kid2=...]
MANAGEMENT_JWT_ISSUERhappy-enterprise-managementRequired iss of management tokens.

Generate a server secret with:

Shell
openssl rand 32 | basenc --base64url | tr -d =

Management

How operators and provisioning scripts authenticate to the management API.

VariableDefaultMeaning
MANAGEMENT_AUTHjwtComma-separated jwt, webauthn, password: how operators and scripts authenticate. Without jwt no JWT is accepted. With password, nothing but setup is served until an operator has a password.
ADMIN_USERNAME, ADMIN_PASSWORDadmin; noneThe first operator, created at startup while no operator has a password (needs password in MANAGEMENT_AUTH). Skips the setup code. Remove ADMIN_PASSWORD afterwards.
MANAGEMENT_WEBAUTHN_RP_IDrequired with webauthnRelying party id of operator keys: the host of the console’s origins, or a parent domain.
MANAGEMENT_WEBAUTHN_ORIGINrequired with webauthnComma-separated origins the console runs on (no paths). The first builds enrollment links.
MANAGEMENT_WEBAUTHN_RP_NAMEHappy Enterprise managementName shown by authenticators.
MANAGEMENT_WEBAUTHN_AAGUIDSnoneComma-separated AAGUIDs allowed to enroll and sign in; needs MANAGEMENT_WEBAUTHN_ATTESTATION_CA. Unset: any authenticator with user verification.
MANAGEMENT_WEBAUTHN_ATTESTATION_CAnonePath of a PEM bundle of attestation root certificates, such as Yubico’s FIDO root, that allowed keys’ attestations must chain to. Set with MANAGEMENT_WEBAUTHN_AAGUIDS.
MANAGEMENT_SESSION_TTL900Seconds an operator session lasts at most.
MANAGEMENT_SESSION_IDLE300Seconds without a request after which an operator session ends.
MANAGEMENT_STEP_UP_WINDOW300Seconds after a security-key assertion during which step-up operations are allowed.

Organization passwords

VariableDefaultMeaning
ORG_PASSWORD_AUTHfalsetrue lets organization users sign in with passwords. Off, every password route is 404; stored passwords are kept.

Addresses and sign-up

VariableDefaultMeaning
ORG_RESERVED_SLUGSnoneComma-separated slugs no organization may take, on top of the built-in list.
ORG_SIGNUPoffopen lets anyone with a proven address create an organization (sign-up). Needs a real email provider.
ORG_SIGNUP_EMAIL_DOMAINSnoneComma-separated domains whose addresses may sign up. Unset: any.
ORG_SIGNUP_DAILY_LIMIT3Organizations one address may create per day.
TURNSTILE_SECRET_KEY, TURNSTILE_SITE_KEYnoneCloudflare Turnstile in front of server-wide sign-in codes (POST /v1/auth/start). Set both or neither.

WebAuthn

VariableDefaultMeaning
WEBAUTHN_RP_IDhost of PUBLIC_BASE_URLRelying party id: a domain name (an IP address fails at startup), the origin’s host or a parent of it.
WEBAUTHN_ORIGINorigin of PUBLIC_BASE_URLOrigin WebAuthn ceremonies run on.
WEBAUTHN_RP_NAMEHappy EnterpriseName shown by authenticators.

Security keys and passkeys are bound to the relying party id. Changing it later makes existing WebAuthn factors unusable.

Email

VariableDefaultMeaning
EMAIL_PROVIDERloglog (logs messages, including login codes: for development, or for password-only setups), resend, ses, cloudflare, smtp, webhook.
EMAIL_FROMrequired unless logSender address.
RESEND_API_KEYrequired for resendResend API key.
RESEND_API_URLhttps://api.resend.comResend API base; /emails is appended.
CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_EMAIL_API_TOKENrequired for cloudflareCloudflare Email Sending: the account id and an API token allowed to send email. EMAIL_FROM must be on a domain onboarded to Email Sending in that account.
CLOUDFLARE_API_URLhttps://api.cloudflare.com/client/v4Cloudflare API base.
SES_REGION, SES_ACCESS_KEY_ID, SES_SECRET_ACCESS_KEYrequired for sesAmazon SES v2 SendEmail, signed with SigV4.
SES_SESSION_TOKEN, SES_ENDPOINTnoneTemporary credentials; a custom endpoint.
SMTP_URLrequired for smtpe.g. smtps://user:pass@smtp.example.com or smtp://host:587?tls=required.
EMAIL_WEBHOOK_URLrequired for webhookReceives a POST of {from, to, subject, text, html}.
EMAIL_WEBHOOK_SECRETnoneSigns webhook requests (see Files and email).

Storage

VariableDefaultMeaning
STORAGE_BACKENDlocallocal or s3.
STORAGE_LOCAL_DIRdata/filesRoot directory of the local store.
S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYrequired for s3S3 (or compatible) bucket.
S3_ENDPOINT, S3_PREFIX, S3_SESSION_TOKENnoneCustom endpoint (path-style, e.g. MinIO, R2), key prefix (put in front of <organization id>/<file id> as it is, so end it with /), temporary credentials.

Actions

VariableDefaultMeaning
ACTION_WEBHOOKS_ALLOW_PRIVATEfalsetrue lets action webhooks use http and reach private and loopback addresses. Development only.

Connectors

VariableDefaultMeaning
CONNECTORS_ALLOW_HTTPfalsetrue lets connectors and their OAuth servers use http. Development only.
CONNECTORS_ALLOW_ADDRESSESnoneComma-separated CIDRs (10.0.0.0/8, 192.168.1.5) and host names that connectors may reach although they are not public, such as an MCP server on your internal network. Everything else that is loopback, private, link-local (cloud metadata included), shared, or reserved is refused.

Validators and logging

VariableDefaultMeaning
MONTY_WORKERhappy-enterprise-monty-worker next to the server binary, if presentSandbox worker for custom validators; off disables them (writes that need them get 503).
RUST_LOGinfotracing filter, e.g. warn,happy_enterprise=debug.

A production example

Shell
ORG_DATABASE_URL=postgres://happy_enterprise_org:...@db.internal/happy_enterprise
MANAGEMENT_DATABASE_URL=postgres://happy_enterprise_management:...@db.internal/happy_enterprise
RUN_MIGRATIONS=false
PUBLIC_BASE_URL=https://api.acme.example
CLIENT_IP_HEADER=x-real-ip
SERVER_SECRET_KEY=...
MANAGEMENT_JWT_PUBLIC_KEYS=ops-2026=11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo
EMAIL_PROVIDER=ses
EMAIL_FROM="Acme <no-reply@acme.example>"
SES_REGION=eu-west-1
SES_ACCESS_KEY_ID=...
SES_SECRET_ACCESS_KEY=...
STORAGE_BACKEND=s3
S3_BUCKET=acme-happy-enterprise
S3_REGION=eu-west-1
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...
Esc
↑ ↓ navigate↵ open