Configuration
This is the authoritative reference for every environment variable and configuration knob in yourhq. If you’re trying to figure out what a variable does, whether you need to set it, or why a change you made didn’t take effect, start here. Related reading:- Networking — deep dive on
NETWORKING_MODEand host vs container networking - Agents — agent-specific runtime config (not covered here; lives in
agent.jsonper branch) - Features — product-level explanation of workspace features and gateway management
1. Where configuration lives
All stack-level config lives in a single file:.env at the repo root (or at $YOURHQ_HOME/.env, typically ~/.yourhq/.env, if you installed via the curl installer). Docker Compose loads this file automatically — the values are substituted into docker-compose.yml (and the dev overlay docker-compose.dev.yml) at docker compose up time.
The installer (installer/install.sh) prompts for the minimum required values and writes .env for you with sensible defaults for everything else. .env.example is the canonical list of supported keys — if a variable isn’t there, it isn’t read by the stack.
There are two flavors of variable that behave very differently:
- Runtime variables — read by the container process when it starts. Change the value in
.env, rundocker compose up -d <service>, and the new value takes effect. Most variables are runtime. - Build-time variables — baked into the container image when
docker compose buildruns. Changing the value in.envdoes nothing until the image is rebuilt. HQ no longer usesNEXT_PUBLIC_SUPABASE_*for project config; Supabase browser config is injected at runtime from the workspace registry.
docker compose pull (and falls back to build if the image isn’t in the registry), so on a fresh install the distinction is invisible. It only matters when you edit .env later.
2. Grouped environment variable reference
Each variable below lists: Name / Required / Default / What it does / When to override.2.1 Supabase (managed by the UI, not .env)
The gateway, dispatcher, and runner each read Supabase credentials from two sources in this order:
- Environment variables (
SUPABASE_URL+SUPABASE_SERVICE_ROLE_KEY) if set. - Workspace registry at
/config/workspaces.json+/config/secrets.json(written by the UI’s onboarding screen and mounted read-only into the gateway services).
.env for Supabase. The onboarding screen writes the creds to the shared ui-config volume, the gateway polls /config on boot and picks them up automatically.
Use (1) only as an override — e.g. a remote gateway that must always point at a specific database regardless of what the UI’s active workspace is.
-
SUPABASE_URL— Optional override. No default. If set, takes precedence over the registry. -
SUPABASE_SERVICE_ROLE_KEY— Optional override, secret. No default. If set, takes precedence over the registry. -
EMBEDDER_URL— Optional. Defaults tohttp://embedder:18801inside Compose. Agent scripts use this local HTTP endpoint for query/document embeddings. -
EMBEDDER_MODEL— Optional. Defaults toBAAI/bge-small-en-v1.5. The default model (~45 MB) is pre-loaded in the embedder Docker image for instant startup. Override with any fastembed-compatible model name — non-default models download on first boot and are cached in thegateway-embedding-cachevolume for subsequent restarts. -
EMBEDDER_CHUNK_CHARS,EMBEDDER_CHUNK_OVERLAP,EMBEDDER_MAX_CHUNKS— Optional. Defaults to3500,500, and500. These tune the source-agnostic knowledge chunker used for documents now and future file/external-source indexing.
/config/workspaces.json for URL + anon key, /config/secrets.json for service role key). Don’t edit these by hand — use the onboarding screen on first boot, or Settings → Database afterward.
secrets.json is mode 0644 inside the Docker-managed config volume so the UI, gateway, runner, and dispatcher containers can all read it despite running as different users. The host-level Docker volume directory is the real filesystem boundary. Treat the host and every container in the Compose stack as trusted.
2.2 Gateway identity
These let one Supabase database host multiple independent gateway hosts (your laptop, a Mac mini, a VPS) without them stepping on each other.-
GATEWAY_ID— Required. Defaultdefault. Unique slug per gateway within a Supabase database. Becomes theslugcolumn in thegatewaystable. Override when you bring up a second gateway against the same Supabase (e.g.mac-mini,vps-eu). -
GATEWAY_LABEL— Optional. DefaultPrimary gateway. Human-readable label shown in the UI’s gateway picker. Override for anything you’d show a person. -
WORKSPACE_SLUG— Required. Defaultmy-workspace. The workspace slug you pick in the setup wizard. Used as the prefix for per-agent git branches in the gateway’s local repo (branches are named${WORKSPACE_SLUG}/${agent_slug}). Must match the slug in the UI’s workspace record — if you change it after install, rename branches accordingly. -
COMPOSE_PROJECT— Optional. Defaultyourhq. Namespaces Compose containers and volumes (you’ll seeyourhq-ui,yourhq-gateway, etc.). Override if you’re running two parallel stacks on one host (e.g.yourhq-staging).
2.3 Networking
Host-level networking decisions: local-only vs Tailscale vs public HTTPS. The installer sets all five of these together based on the mode you pick. See Networking for the full topology.-
NETWORKING_MODE— Required. Defaultlocal. One oflocal/tailscale/public. Purely informational for the containers — it’s written into thegateways.meta.networking_modecolumn so the UI knows how to build URLs. The actual binding behavior is controlled by the*_HOST_PORTvariables below. -
HOST_REACHABLE_URL— Required. Defaulthttp://localhost. The URL users hit in their browser, minus the port. Installer sets this tohttp://localhostforlocal,http://<host-ts-ip>fortailscale, orhttps://<your-domain>forpublic. The gateway writes it into Supabase so the UI can build links to noVNC and the files-API. -
UI_HOST_PORT— Optional. Default127.0.0.1:3000(loopback-only, safe). The installer flips this to0.0.0.0:3000when you pick Tailscale or Public mode. Manually override to0.0.0.0:3000when you want the host’s network interfaces (Tailscale, public) to decide reachability. -
FILES_API_HOST_PORT— Optional. Default127.0.0.1:18790(loopback-only). The files-API only needs to be reachable from outside the host when the UI lives on a different machine (Tailscale cross-host). Single-host installs can leave it on loopback — the UI reaches it internally via Docker DNS (http://gateway:18790). -
NOVNC_BIND— Optional. Defaultlocal. One oflocal(websockify binds0.0.0.0:6901inside the container; the UI proxies it through/api/novncover Docker’s internal network) oroff(don’t start noVNC at all). Port 6901 is not mapped to the host. Override tooffif you don’t need remote desktop. -
VNC_PASSWORD— Optional, secret. Empty. The VNC password for the in-container desktop. Auto-generated on first boot if empty; the generated value is saved to$OPENCLAW_HOME/.vnc-passwordinside thegateway-statevolume. Override if you want a specific known password.
2.4 Files-API (UI file browser)
The gateway runs an HTTP API (files_api.py) that the UI calls to read/write files inside per-agent git worktrees. Auth is a shared bearer token.
-
GATEWAY_AUTH_TOKEN— Required (secret) if you want the file browser. Empty by default. Shared secret between the UI and the gateway’s files-API. Generate withopenssl rand -hex 32. Must be identical in both theuiandgatewayservices — since both read the same.env, this is automatic. If empty, the gateway doesn’t start the files-API at all. -
GATEWAY_URL— Optional. Defaulthttp://gateway:18790. Where the UI reaches the files-API. Leave at the default for same-host Compose stacks (uses Docker’s internal DNS). Override tohttp://<gateway-host-ts-ip>:18790when UI and gateway live on different machines connected over Tailscale. -
FILES_API_BIND— Optional. Defaultdocker. One ofdocker(listens on the Docker internal network only — UI reaches viagateway:18790),any(binds0.0.0.0inside container; host port-mapping decides exposure), oroff(disable the files-API). Override toanywhen the UI is on a remote host. -
FILES_API_PORT— Optional. Default18790. In-container port for the files-API. Rarely worth changing.
2.5 Templates
TEMPLATES_SOURCE— Optional. Empty. When empty, the gateway seeds its bare repo from the templates bundled in/opt/templatesinside the image. Override withgit+https://github.com/your-org/your-templates.gitto seed from your own repo instead. Only read on first boot when the gateway creates its bare repo — changing it later won’t re-seed existing branches.
2.6 Git remote sync (optional, backup)
Lets the gateway push per-agent branches to an external git remote so your agents’ memory and skills are backed up and optionally reachable from other devices. Two configuration paths — pick one. Path A: generic remote (works for any git host — GitHub, Gitea, self-hosted)-
GIT_REMOTE_URL— Optional. Empty. If set, added as theoriginremote of the gateway’s bare repo. Examples:https://x-access-token:[email protected]/org/repo.git,[email protected]:org/repo.git,https://git.example.com/agents.git. -
GIT_DEPLOY_KEY— Optional, secret. Empty. SSH private key (full multiline PEM, keep surrounding double-quotes in.envso newlines survive). Only needed for SSH remotes.
GIT_REMOTE_URL is empty AND all three GITHUB_* vars below are set, the gateway synthesizes https://x-access-token:[email protected]/$GITHUB_REPO_OWNER/$GITHUB_REPO_NAME.git at boot.
-
GITHUB_TOKEN— Optional, secret. Empty. Fine-grained PAT with Contents: Read and write on the target repo. Classic PATs withreposcope also work. -
GITHUB_REPO_OWNER— Optional. Empty. GitHub user or org that owns the repo. -
GITHUB_REPO_NAME— Optional. Empty. Repo name only (no owner prefix).
post-commit git hook on the bare repo that async-pushes every commit to origin. No scheduled sync, no batching — every commit (from add-agent.sh, file-browser edits, or an agent calling save_progress) lands on the remote within seconds. The runner also runs a GIT_SYNC_INTERVAL-second backup sweep that commits dirty worktrees and fast-forwards any branches that moved on the remote.
Without a remote, all of this still works locally — commits just don’t leave the gateway’s volume.
2.7 Remote gateway provisioning
These are only needed when installing a gateway on a separate host from the UI using the gateway installer (install.yourhq.ai/gateway). The normal co-located install doesn’t use them.
-
GATEWAY_TOKEN— Optional, one-time. A short-lived token minted in the UI (Settings → Gateways → Add Gateway). The gateway exchanges it for a stablegateway_id+ slug on first boot viaconsume_gateway_token(). The exchange is one-shot — a.token-consumedmarker file prevents re-exchange on subsequent boots. After the exchange,GATEWAY_IDis pinned to the assigned slug and persisted across reboots. -
SUPABASE_ANON_KEY— Required ifGATEWAY_TOKENis set. The Supabase project’s anon key, used for the token exchange RPC call (which uses the anon role). The service role key is used for everything else.
2.8 Runtime tuning
Control loops in the dispatcher and runner daemons. Defaults are sensible; override only when instrumenting or tuning latency.-
POLL_INTERVAL— Optional. Default30. Seconds between command-queue polls by the runner. Lower for faster command pickup at the cost of more Supabase requests. -
COMMAND_TIMEOUT— Optional. Default120. Seconds a single command can run before the runner kills it and marks it failed. -
RECONCILE_INTERVAL— Optional. Default120. Seconds between dispatcher reconciliation sweeps — the safety-net pass that wakes agents with pending inbox work that Realtime may have missed. -
WAKE_COOLDOWN— Optional. Default30. Minimum seconds between successive wake calls for the same agent. Prevents the dispatcher from spamming an agent with rapid wakes when multiple inbox items arrive in a burst. -
GIT_SYNC_INTERVAL— Optional. Default1800(30 min). Seconds between backup-sweep passes in the runner (commits dirty worktrees, fast-forwards branches that moved on the remote). Set to0to disable the sweep entirely — event-driven commits and the post-commit auto-push still work. -
RUNTIME_MODE— Optional. Defaultdocker. One ofdocker,systemd, ore2b. Controls howrestart_gateway,restart_dispatcher, andupdate_gatewaycommands are executed.dockerusesdocker composecommands;systemdusesopenclaw gateway restartandsystemctl;e2badapts the entrypoint for cloud sandbox environments. Self-hosted installs should usedocker(default). -
TENANT_ID— Optional. Default00000000-0000-0000-0000-000000000000. The UUID of the tenant row this gateway and its agents belong to. Self-hosted installs use the default nil UUID. Override only if you’re running a multi-tenant setup.
2.9 Gateway internals
These are rarely needed but documented for completeness and troubleshooting.-
OPENCLAW_HOME— Optional. Default~/.openclaw. The directory where OpenClaw stores its state:openclaw.json, the bare git repo, agent branches, auth profiles, and the VNC password. Must be inside thegateway-stateDocker volume to persist across container restarts. -
DEBUG— Optional. Default unset. Set to1to enableset -xin the gateway entrypoint, printing every shell command to the container log. Noisy but useful for diagnosing boot failures.
2.9 Image overrides
Pin specific image tags for reproducible production deploys. All four default to:latest.
UI_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-ui:latest.GATEWAY_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-gateway:latest.DISPATCHER_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-dispatcher:latest.RUNNER_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-runner:latest.EMBEDDER_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-embedder:latest.FILE_PROCESSOR_IMAGE— Optional. Defaultghcr.io/yourhq/yourhq-file-processor:latest.
ghcr.io/yourhq/yourhq-<svc>:2026.04.20 (or similar date-pinned tag) for production. Don’t pin in dev — you want to track :latest.
2.10 Codespaces / reverse-proxy origin allowlist
Next.js enforces an origin allowlist on server actions and form submissions. When the UI is fronted by a reverse proxy or GitHub Codespaces port-forward, the apparent origin differs fromlocalhost:3000 and requests get rejected as CSRF. These variables extend the allowlist.
-
CODESPACE_NAME— Auto-set by GitHub Codespaces. Don’t set manually. -
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN— Auto-set by GitHub Codespaces. Don’t set manually. Combined withCODESPACE_NAMEby the UI to whitelist the forwarded URL. -
ALLOWED_ORIGINS— Optional. Empty. Comma-separated list of extra origins to allow (e.g.https://hq.example.com,https://staging.example.com). Set this when running behind any non-Codespaces reverse proxy with a public domain.
3. Runtime Supabase config — no more NEXT_PUBLIC bake-in
The UI used to bakeNEXT_PUBLIC_SUPABASE_* into the client bundle at build time, which meant every user had to docker compose build ui locally to stamp in their own Supabase values. That’s gone.
Now the UI reads its Supabase config at runtime from the workspace registry (/config/workspaces.json + /config/secrets.json). The root layout renders a <script>window.__HQ_CONFIG__ = {...}</script> tag based on the active workspace’s cookie, so the client bundle picks up whoever’s connected without a rebuild.
Practical consequences:
- The GHCR
yourhq-ui:latestimage works for every user. Nodocker compose build uirequired — ever. - Changing Supabase creds is a UI action (Settings → Database → Rotate), not a rebuild.
NEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEYenv vars are no longer read by the UI. Leave them unset.
2.11 Secrets encryption
These variables control how user-managed secrets (channel tokens, API keys, OAuth tokens) are encrypted in thesecrets table and decrypted on the gateway.
-
HOSTED_SECRETS_KEY— Optional (hosted only), secret. Empty. A 32-byte encryption key (base64url, base64, or hex encoded) used for AES-256-GCM encryption of secrets. When set, takes precedence over the HKDF-derived key. Used by the hosted offering where the service role key is not a stable derivation source. -
SOURCE_SYNC_POLL_INTERVAL— Optional. Default60. Seconds between source-sync poll cycles (checkssource_connectionsfor connections due for sync). Not secrets-specific but relevant because source sync now reads OAuth tokens from the secrets filesystem.
HOSTED_SECRETS_KEY — the encryption key is automatically derived from SUPABASE_SERVICE_ROLE_KEY via HKDF-SHA256 (salt: yourhq-secrets-v1, info: aes-256-gcm, 32 bytes). This means rotating your service role key requires re-encrypting all secrets.
2.12 Hosted-only observability (Sentry + PostHog)
These services are only active in the hosted offering (DEPLOYMENT_MODE=hosted). Self-hosted deployments never load the Sentry or PostHog SDKs, never send telemetry, and never need these variables. If you’re self-hosting, skip this section entirely.
Sentry (error tracking):
NEXT_PUBLIC_SENTRY_DSN— Hosted only. Empty. The Sentry DSN used by the UI (client, server, and edge runtimes). Gated onDEPLOYMENT_MODE=hosted+NODE_ENV=production— absent or empty means no Sentry code runs.SENTRY_DSN— Hosted only. Empty. The Sentry DSN used by the worker and passed to E2B gateway sandboxes. Same DSN value as above, but not prefixed withNEXT_PUBLIC_because these runtimes don’t need Next.js client bundling.SENTRY_AUTH_TOKEN— Hosted only, build-time (Vercel). A Sentry personal API token withproject:releasesandorg:readscopes. Used by@sentry/nextjsduring the Next.js build to upload source maps. Not needed at runtime.SENTRY_ORG— Hosted only, build-time (Vercel). The Sentry org slug.SENTRY_PROJECT— Hosted only, build-time (Vercel). The Sentry project slug.
SENTRY_DSN and additionally require RUNTIME_MODE to be hosted or e2b before initializing Sentry. Self-hosted gateways (RUNTIME_MODE=docker) never activate Sentry even if a DSN is present.
PostHog (product analytics):
NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN— Hosted only. Empty. PostHog project API key for the UI client.NEXT_PUBLIC_POSTHOG_HOST— Hosted only. Defaulthttps://us.i.posthog.com. PostHog ingest endpoint.POSTHOG_API_KEY— Hosted only. Empty. Server-side PostHog API key for the worker.
DEPLOYMENT_MODE=hosted. No analytics code runs in self-hosted installs.
4. Secrets handling
Stack-level secrets (Supabase keys, gateway auth tokens) live in plaintext in.env on disk. The installer chmod 600 the file, which is the only layer of protection. Don’t commit .env, don’t paste it into issues, don’t ship it in backups unencrypted.
User-managed secrets (API keys, channel tokens, OAuth tokens) are stored encrypted in the secrets table using AES-256-GCM. They are never in .env on the UI host. On the gateway, they’re decrypted and written to ~/.openclaw/secrets/ as per-agent .env files (chmod 0600). These derived files are regenerated on every change and do not need manual management. See Secrets management for the full architecture.
If one of these leaks:
-
SUPABASE_SERVICE_ROLE_KEY— bypasses RLS and can read/write every row. High severity. Rotate in Supabase dashboard (Project Settings → API → “Generate new service_role key”). Update the affected workspace in Settings → Database, or update.envif you use env overrides. Restart gateway services after changing env overrides. Auditaudit_logfor suspicious activity. -
UI anon key (stored in
/config/workspaces.jsonper workspace) — subject to RLS, so damage is bounded by your policies. Rotate in the Supabase dashboard, then update it via Settings → Database → the affected workspace in the UI. -
GATEWAY_AUTH_TOKEN— auth between UI and files-API. Rotate by picking a new value (openssl rand -hex 32), writing it to.env, and runningdocker compose up -d ui gateway(both services must restart together so they agree on the token). -
GIT_DEPLOY_KEY— rotate in the git host (GitHub → repo settings → Deploy keys), put the new key in.env, thendocker compose up -d gateway. The gateway recreates~/.ssh/openclaw_deploy_keyif it’s missing, but it doesn’t overwrite an existing one — you may need todocker compose exec gateway rm /home/openclaw/.ssh/openclaw_deploy_keyfirst. -
GITHUB_TOKEN— rotate the PAT on GitHub (or revoke it), update.env, thendocker compose up -d gatewayso the entrypoint rebuilds the remote URL with the new token. Existing commits stay local even if the token is revoked — they push to the new remote on the next boot. -
VNC_PASSWORD— rotate by changing the value in.env, then delete the existing hash:docker compose exec gateway rm /home/openclaw/.vnc/passwd, thendocker compose up -d gateway. -
TAILSCALE_AUTH_KEY— host-level, not container-level. The stack no longer runs Tailscale inside any container. The auth key is used once by the installer to runtailscale upon the host. If leaked, revoke in the Tailscale admin console (Settings → Keys) and generate a new one — no container restart needed. Use single-use keys in production and reusable keys only for convenience during setup.
5. Configuring for development
docker-compose.dev.yml is an overlay that runs the UI in next dev mode with source mounted for live-reload, and mounts the gateway daemons from source so edits take effect on restart (no rebuild). Usage:
node:24-slim instead of the prebuilt UI image). Your .env works as-is. Supabase creds for the UI come from the workspace registry in both modes, so there’s no “did I rebuild” confusion.
6. First-boot setup (non-env configuration)
A lot of yourhq’s configuration is runtime-configurable via the UI, not via environment variables. Once the stack is up and you’ve logged in, visit:-
/onboarding— the unified onboarding wizard (welcome → intent → infrastructure → provider → agent). Seeds workspace identity, context preset, model provider connection, and first agent in one flow. New workspaces are redirected here automatically until onboarding is complete. Progress persists across page refreshes via sessionStorage. -
/dashboard/settings— add or edit pipeline stages, custom field definitions, streams, connections, and agent identities after onboarding. Routines are managed from/dashboard/routines. Most per-workspace behavior lives here, not in.env. -
/dashboard/agents— the agent creation wizard (template → identity → channel). See Agents for agent-specific configuration (each agent has its ownagent.jsoncommitted to its git branch).
.env.
7. Changing configuration after install
Edit.env, then run the appropriate Compose command to pick up the change. The right command depends on which service owns the variable and whether it’s build-time or runtime.
UI runtime variables (non-NEXT_PUBLIC_*)
SUPABASE_SERVICE_ROLE_KEY, GATEWAY_URL, GATEWAY_AUTH_TOKEN, ALLOWED_ORIGINS:
UI workspace config (no rebuild required)
Supabase URL, anon key, service role key per workspace live in the workspace registry on theui-config volume, not in .env. Change them via the UI (Settings → Database → Edit / Rotate) — takes effect immediately on the next request.
Gateway variables
TEMPLATES_SOURCE, GIT_REMOTE_URL, GIT_DEPLOY_KEY, GITHUB_TOKEN, GITHUB_REPO_OWNER, GITHUB_REPO_NAME, GATEWAY_AUTH_TOKEN, FILES_API_BIND, FILES_API_PORT, NOVNC_BIND, VNC_PASSWORD, NETWORKING_MODE, HOST_REACHABLE_URL:
TEMPLATES_SOURCE is only read on first boot when the bare repo is seeded. Changing it later has no effect unless you also wipe the gateway-state volume (destructive — you lose all agent branches).
Dispatcher / runner variables
POLL_INTERVAL, COMMAND_TIMEOUT, RECONCILE_INTERVAL, WAKE_COOLDOWN:
Networking / port-binding changes
UI_HOST_PORT, FILES_API_HOST_PORT — changing a port binding doesn’t register with a plain up -d; Compose considers the container “already running” with no relevant change. Force recreation:
Compose project / gateway identity
COMPOSE_PROJECT, GATEWAY_ID — changing either creates new containers and (for COMPOSE_PROJECT) new volumes. Your old stack’s state is orphaned but not deleted. Only change these when you actually want a fresh namespace; otherwise the safer path is to leave them alone.
Image overrides
UI_IMAGE, GATEWAY_IMAGE, DISPATCHER_IMAGE, RUNNER_IMAGE:
Gateway Supabase keys
Changing the gateway’s Supabase connection (SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY) affects the gateway, dispatcher, and runner. UI Supabase creds live in the workspace registry and don’t need any restart:
Troubleshooting checklist
- “The UI loads but login fails” — the active workspace in the registry may have wrong creds. Open Settings → Database → Edit or Rotate to fix, no restart needed.
- “File browser says 401 / auth error” —
GATEWAY_AUTH_TOKENdiffers between UI and gateway, or is empty. Set it identically in.envand rundocker compose up -d ui gateway. - “Changed the port but the old one still works” — port bindings need
--force-recreate(section 7). - “Changed
TEMPLATES_SOURCEbut I still see old templates” — it’s only read on first boot. See section 2.5. - “Gateway keeps trying to reach the wrong URL” —
HOST_REACHABLE_URLis written into Supabase by the gateway at startup; fix.envand restart the gateway, not the UI.

