Configuration reference
Hermeum is configured entirely through HERMEUM_* environment variables. The
Hermeum reads them once at startup via a Zod schema
(apps/app/src/server/libs/config.ts); invalid or missing required
values fail fast with a parse error before the server binds a port.
The Helm chart maps these to secrets.* (sensitive) and config.*
(non-sensitive) values — see Installation. Every variable
below is optional unless marked required.
Environment variables
Required variables must be set or the Hermeum pod refuses to start. All others fall back to the defaults listed here.
Core
| Variable | Default | Description |
|---|---|---|
HERMEUM_DATABASE_URL | — | Connection URL for the Hermeum database. For sqlite use file:/path/to/db.sqlite; for postgres use a postgres:// URL. See Database. |
HERMEUM_DATABASE_DIALECT | sqlite | Database backend dialect. One of sqlite, postgres. Must match the scheme of HERMEUM_DATABASE_URL. |
HERMEUM_KUBERNETES_NAMESPACE | hermeum | Kubernetes namespace where HermesAgent custom resources are reconciled. Hermeum must have RBAC to read/write this namespace. |
HERMEUM_CONFIG_PATH | ./config.yaml | Path to the Hermeum instance config file. See Instance config for the schema. |
HERMEUM_HERMES_DOCS_PATH | ./docs | Path to the hermes docs directory, scanned recursively by the docs file adaptor. |
HERMEUM_LOG_LEVEL | info | Log verbosity. One of debug, info, warn, error. |
Agent image
The container image emitted into every HermesAgent CR's spec.image.
| Variable | Default | Description |
|---|---|---|
HERMEUM_HERMES_IMAGE_REPOSITORY | nousresearch/hermes-agent | Container image repository for the Hermes agent. Omit the registry host for Docker Hub, or include it (e.g. ghcr.io/hermeum/hermes-agent) for other registries. |
HERMEUM_HERMES_IMAGE_TAG | v2026.9.21 | Container image tag for the Hermes agent. Pin to a specific release for reproducible agent pods. |
Agent-session telemetry
Every agent managed by Hermeum automatically installs the hermeum
plugin (via the
operator's hermes plugins install), which forwards agent-session events to
Hermeum's plugin-protocol endpoint. The install is pinned to a fixed Hermeum
commit shipped with the app, so it does not drift with the repository's
default branch.
The endpoint URL is derived from the base
URL below; it is injected as HERMEUM_PLUGIN_BASE_URL into the agent's
workspace .env and always wins over a user-set agent env var of the same
name. The endpoint is authenticated: at agent-create time Hermeum provisions
each agent with a per-agent ingest token (injected as the sensitive
HERMEUM_PLUGIN_TOKEN env var, its SHA-256 digest stored in the app database)
and rejects every request — including the health probe — without a valid
token. Archiving an agent revokes its token. Nothing here is
operator-configurable; the token lifecycle is fully automatic.
| Variable | Default | Description |
|---|---|---|
HERMEUM_PLUGIN_BASE_URL | http://hermeum:3000 | Base URL agents use to reach the Hermeum app. The hermeum plugin posts events to <pluginBaseUrl>/plugin/trpc/agentSession.agentSessionEvents. The Helm chart defaults this to the app's in-cluster Service DNS (http://<release-name>:<config.port>); override for cross-namespace or proxied routes. |
HERMEUM_AGENT_SESSION_RETENTION_DAYS | 90 | Retention window, in days, for agent-session telemetry. A scheduled job (six-hour cycle) deletes every session whose most recent event is older than the window, together with all of its events — sessions are pruned whole, never partially. Set 0 to disable retention and keep every session forever. |
Auth
Better Auth email/password, allowed email domains, and outgoing SMTP. Better
Auth's own secret (BETTER_AUTH_SECRET) is provided via Helm secrets.* and
is not a HERMEUM_* variable.
| Variable | Default | Description |
|---|---|---|
HERMEUM_SMTP_URL | — | SMTP server URL for outgoing email (e.g. smtps://user:pass@smtp.example.com:465). Optional. |
HERMEUM_SMTP_FROM | — | From address for outgoing email, e.g. Hermeum <no-reply@example.com>. Set it when HERMEUM_SMTP_URL is configured — most SMTP providers reject or spam-filter messages without a From header. Optional. |
HERMEUM_ALLOWED_EMAIL_DOMAIN | — | Restrict sign-ups to this email domain (e.g. example.com). Optional; when unset, any domain is accepted. |
AI config generator
The AI config generator uses an OpenAI-compatible chat completion endpoint to
draft agent config.yaml blocks from natural-language prompts.
| Variable | Default | Description |
|---|---|---|
HERMEUM_OPENAI_MODEL | gpt-6.1-sol | Model id passed to the completion call. Must be a model the configured endpoint serves. |
HERMEUM_OPENAI_BASE_URL | — | Override the OpenAI API base URL. Point this at any OpenAI-compatible gateway (vLLM, Ollama, Azure OpenAI, etc.). When unset, the OpenAI default is used. |
HERMEUM_OPENAI_API_KEY | — | API key for the OpenAI-compatible endpoint. Required when targeting the hosted OpenAI API; may be unused for local gateways. |
Per-agent ingress
When HERMEUM_AGENT_INGRESS_BASE_HOSTNAME is set, Hermeum emits an
Ingress per agent that routes each enabled HTTP platform on its own host
<agent-id>.<platform-label>.<base hostname> (api-server → api, webhook
→ hooks, teams → teams) — or, with
HERMEUM_AGENT_INGRESS_FLATTEN_HOSTS=true,
<agent-id>-<platform-label>.<base hostname> — with each host mapped
wholesale to the platform's Service port. When it is unset, no ingress is
generated.
| Variable | Default | Description |
|---|---|---|
HERMEUM_AGENT_INGRESS_BASE_HOSTNAME | — | Base hostname for per-agent ingresses; each HTTP platform is exposed at <agent-id>.<platform-label>.<base> (api / hooks / teams) — or <agent-id>-<platform-label>.<base> with HERMEUM_AGENT_INGRESS_FLATTEN_HOSTS=true. Unset = no ingress generated. |
HERMEUM_AGENT_INGRESS_FLATTEN_HOSTS | false | Flatten agent ingress hosts to a single DNS level: <agent-id>-<platform-label>.<base> instead of <agent-id>.<platform-label>.<base>. One wildcard record/cert *.<base> then covers every agent and platform. |
HERMEUM_AGENT_INGRESS_SCHEME | http | Public URL scheme advertised for agent ingresses. Display-only — it does not drive the emitted tls block; TLS is governed by HERMEUM_AGENT_INGRESS_TLS_SECRET_NAME. |
HERMEUM_AGENT_INGRESS_CLASS_NAME | — | Ingress controller class name set on generated ingresses (spec.ingressClassName). Omitted from the CR when unset. |
HERMEUM_AGENT_INGRESS_TLS_SECRET_NAME | — | TLS secret name for controller-terminated TLS. When set, the ingress emits a tls block with this secret; when unset, no tls block is emitted (covers plain HTTP and load-balancer-terminated TLS). |
See Per-agent ingress and TLS for end-to-end TLS topologies.
Web server
Hermeum's own HTTP(S) listener.
| Variable | Default | Description |
|---|---|---|
HERMEUM_PORT | 3000 | Port the web server listens on. |
HERMEUM_HMR_PORT | 3001 | Port used by Vite's HMR websocket in dev. Ignored in production. |
HERMEUM_TLS_CERT_FILE | — | Path to the web TLS cert file (PEM). When set with HERMEUM_TLS_KEY_FILE, the web server serves HTTPS on HERMEUM_PORT. |
HERMEUM_TLS_KEY_FILE | — | Path to the web TLS key file (PEM). Pair with HERMEUM_TLS_CERT_FILE. |
Webhook
The mutating admission webhook's HTTPS listener. It only starts when both
HERMEUM_WEBHOOK_TLS_CERT_FILE and HERMEUM_WEBHOOK_TLS_KEY_FILE are set;
otherwise the webhook is not served.
| Variable | Default | Description |
|---|---|---|
HERMEUM_WEBHOOK_PORT | 8443 | HTTPS port for the mutating admission webhook. Only used when the webhook TLS cert and key are set. |
HERMEUM_WEBHOOK_TLS_CERT_FILE | — | Path to the mutating webhook TLS cert file (PEM). Pair with HERMEUM_WEBHOOK_TLS_KEY_FILE to start the webhook listener. |
HERMEUM_WEBHOOK_TLS_KEY_FILE | — | Path to the mutating webhook TLS key file (PEM). Pair with HERMEUM_WEBHOOK_TLS_CERT_FILE. |
See Mutating webhook for how the webhook is wired into
the cluster and how its caBundle gets populated.
Telemetry
Hermeum's telemetry adaptor logs to the console and reports a lightweight
deployment heartbeat (deployment.heartbeat event) to a Hermeum-owned PostHog
project so the team can count running self-hosted servers and track version
adoption. The heartbeat is captured as a PostHog event on startup and then
hourly, and carries only anonymous deployment-level properties: the deployment
id, hosting, the app version, and the database dialect. The heartbeat is
always reported and is not governed by the log-mirroring toggle below.
Optionally, console log messages can also be mirrored to PostHog Logs via the official PostHog OpenTelemetry (OTLP) integration.
| Variable | Default | Description |
|---|---|---|
HERMEUM_POSTHOG_API_KEY | Hermeum's project token | PostHog project API key (phc_...), baked in as the default so heartbeats work out of the box. The token is public by design — override to report to a different PostHog project. |
HERMEUM_POSTHOG_HOST | https://us.i.posthog.com | PostHog ingestion host. |
HERMEUM_DEPLOYMENT_ID | — | Stable identifier for this deployment, used as the heartbeat's distinct id. When unset, a random id is generated per process boot — set this (e.g. to the Helm release name) for accurate server counts across restarts. |
HERMEUM_TELEMETRY_DISABLED | false | Set to true to disable log mirroring to PostHog Logs. Console output is always enabled; the deployment heartbeat event is not affected. |
RBAC notes
Hermeum runs as a Kubernetes controller for HermesAgent CRs inside
HERMEUM_KUBERNETES_NAMESPACE. The chart's ServiceAccount grants the
following scoped permissions:
hermesagents(custom resource): full CRUD within the configured namespace — Hermeum creates, patches, and finalizes agent CRs.secrets: read/write within the namespace, used to materialize per-agent image-pull secrets and TLS material referenced by ingresses.ingresses(networking.k8s.io): create/update/delete within the namespace when per-agent ingresses are enabled (HERMEUM_AGENT_INGRESS_BASE_HOSTNAMEset).pods/pods/log: read, for status display and log streaming from agent pods.
When per-agent ingress is disabled (no base hostname), the ingresses grant
is unused in practice. If you deploy the chart into a namespace you do not own
and instead grant a narrower Role manually, ensure the four resource groups
above are still covered or the corresponding features will return RBAC
forbidden errors at runtime.
The mutating admission webhook is served by the same pod but on a separate
port (HERMEUM_WEBHOOK_PORT); it is authorized through the
MutatingWebhookConfiguration's clientConfig.service path, not through the
pod ServiceAccount.