> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yourhq.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

# 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](/self-host/networking) — deep dive on `NETWORKING_MODE` and host vs container networking
* [Agents](/concepts/agents) — agent-specific runtime config (not covered here; lives in `agent.json` per branch)
* [Features](/concepts/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`, run `docker 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 build` runs. Changing the value in `.env` does nothing until the image is rebuilt. HQ no longer uses `NEXT_PUBLIC_SUPABASE_*` for project config; Supabase browser config is injected at runtime from the workspace registry.

The installer always runs `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:

1. **Environment variables** (`SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY`) if set.
2. **Workspace registry** at `/config/workspaces.json` + `/config/secrets.json` (written by the UI's onboarding screen and mounted read-only into the gateway services).

The normal path is (2) — you never touch `.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 to `http://embedder:18801` inside Compose. Agent scripts use this local HTTP endpoint for query/document embeddings.

* **`EMBEDDER_MODEL`** — Optional. Defaults to `BAAI/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](https://qdrant.github.io/fastembed/examples/Supported_Models/) — non-default models download on first boot and are cached in the `gateway-embedding-cache` volume for subsequent restarts.

* **`EMBEDDER_PORT`** — Optional. Default `18801` in Docker mode, `9100` in hosted mode. The in-container port the embedder HTTP server listens on. In hosted mode the default shifts to `9100` because `18801` is the first CDP port allocated by `add-agent.sh` and would collide with the first agent's Chrome.

* **`EMBEDDER_CACHE_DIR`** — Optional. Default `/models` (inside the `gateway-embedding-cache` volume in Docker mode), `/opt/yourhq/models` in hosted mode. Directory where the embedder caches downloaded model files. Persisted across restarts so non-default models don't re-download on every boot.

* **`EMBEDDER_CHUNK_CHARS`**, **`EMBEDDER_CHUNK_OVERLAP`**, **`EMBEDDER_MAX_CHUNKS`** — Optional. Defaults to `3500`, `500`, and `500`. These tune the source-agnostic knowledge chunker used for documents now and future file/external-source indexing.

* **`REGISTRY_TIMEOUT`** — Optional. Default `600` (10 minutes). Seconds the gateway waits for the workspace registry (`/config/workspaces.json` + `/config/secrets.json`) to become available on boot. Only relevant for co-located installs where the gateway starts before the UI has completed onboarding. Increase if your first-boot onboarding takes longer than 10 minutes.

**Where the real creds live**: the workspace registry files (`/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**. Default `default`. Unique slug per gateway within a Supabase database. Becomes the `slug` column in the `gateways` table. Override when you bring up a second gateway against the same Supabase (e.g. `mac-mini`, `vps-eu`).

* **`GATEWAY_LABEL`** — **Optional**. Default `Primary gateway`. Human-readable label shown in the UI's gateway picker. Override for anything you'd show a person.

* **`WORKSPACE_SLUG`** — **Required**. Default `my-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**. Default `yourhq`. Namespaces Compose containers and volumes (you'll see `yourhq-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](/self-host/networking) for the full topology.

* **`NETWORKING_MODE`** — **Required**. Default `local`. One of `local` / `tailscale` / `public`. Purely informational for the containers — it's written into the `gateways.meta.networking_mode` column so the UI knows how to build URLs. The actual binding behavior is controlled by the `*_HOST_PORT` variables below.

* **`HOST_REACHABLE_URL`** — **Required**. Default `http://localhost`. The URL users hit in their browser, minus the port. Installer sets this to `http://localhost` for `local`, `http://<host-ts-ip>` for `tailscale`, or `https://<your-domain>` for `public`. The gateway writes it into Supabase so the UI can build links to noVNC and the files-API.

* **`UI_HOST_PORT`** — **Optional**. Default `127.0.0.1:3000` (loopback-only, safe). The installer flips this to `0.0.0.0:3000` when you pick Tailscale or Public mode. Manually override to `0.0.0.0:3000` when you want the host's network interfaces (Tailscale, public) to decide reachability.

* **`FILES_API_HOST_PORT`** — **Optional**. Default `127.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**. Default `local`. One of `local` (websockify binds `0.0.0.0:6901` inside the container; the UI proxies it through `/api/novnc` over Docker's internal network) or `off` (don't start noVNC at all). Port 6901 is not mapped to the host. Override to `off` if 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-password` inside the `gateway-state` volume. 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 with `openssl rand -hex 32`. Must be identical in both the `ui` and `gateway` services — since both read the same `.env`, this is automatic. If empty, the gateway doesn't start the files-API at all.

* **`GATEWAY_URL`** — **Optional**. Default `http://gateway:18790`. Where the UI reaches the files-API. Leave at the default for same-host Compose stacks (uses Docker's internal DNS). Override to `http://<gateway-host-ts-ip>:18790` when UI and gateway live on different machines connected over Tailscale.

* **`FILES_API_BIND`** — **Optional**. Default `docker`. One of `docker` (listens on the Docker internal network only — UI reaches via `gateway:18790`), `any` (binds `0.0.0.0` inside container; host port-mapping decides exposure), or `off` (disable the files-API). Override to `any` when the UI is on a remote host.

* **`FILES_API_PORT`** — **Optional**. Default `18790`. 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/templates` inside the image. Override with `git+https://github.com/your-org/your-templates.git` to 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 the `origin` remote of the gateway's bare repo. Examples: `https://x-access-token:$TOKEN@github.com/org/repo.git`, `git@github.com: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 `.env` so newlines survive). Only needed for SSH remotes.

**Path B: GitHub shorthand** (for GitHub only, no URL construction)

If `GIT_REMOTE_URL` is empty AND all three `GITHUB_*` vars below are set, the gateway synthesizes `https://x-access-token:$GITHUB_TOKEN@github.com/$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 with `repo` scope 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).

**How sync works.** When either path is configured, the gateway installs a `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 stable `gateway_id` + slug on first boot via `consume_gateway_token()`. The exchange is one-shot — a `.token-consumed` marker file prevents re-exchange on subsequent boots. After the exchange, `GATEWAY_ID` is pinned to the assigned slug and persisted across reboots.

* **`SUPABASE_ANON_KEY`** — **Required if `GATEWAY_TOKEN` is 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**. Default `30`. Seconds between command-queue polls by the runner. Lower for faster command pickup at the cost of more Supabase requests.

* **`COMMAND_TIMEOUT`** — **Optional**. Default `120`. Seconds a single command can run before the runner kills it and marks it failed.

* **`RECONCILE_INTERVAL`** — **Optional**. Default `120`. Seconds between dispatcher reconciliation sweeps — the safety-net pass that wakes agents with pending inbox work that Realtime may have missed.

* **`WAKE_COOLDOWN`** — **Optional**. Default `30`. 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**. Default `1800` (30 min). Seconds between backup-sweep passes in the runner (commits dirty worktrees, fast-forwards branches that moved on the remote). Set to `0` to disable the sweep entirely — event-driven commits and the post-commit auto-push still work.

* **`RUNTIME_MODE`** — **Optional**. Default `docker`. One of `docker`, `systemd`, `hosted`, or `e2b`. Controls how `restart_gateway`, `restart_dispatcher`, and `update_gateway` commands are executed and which subsystems the entrypoint starts. `docker` uses `docker compose` commands (daemons run as separate containers); `systemd` uses `openclaw gateway restart` and `systemctl`; `hosted` runs all daemons in-process within a single container (used by the hosted offering's cloud sandboxes); `e2b` is a legacy alias for `hosted`. Additionally, `RUNTIME_MODE` controls Sentry initialization in Python daemons — Sentry only activates when the mode is `hosted` or `e2b`, never in self-hosted installs. Self-hosted installs should use `docker` (default).

* **`TENANT_ID`** — **Optional**. Default `00000000-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 the `gateway-state` Docker volume to persist across container restarts.

* **`DEBUG`** — **Optional**. Default unset. Set to `1` to enable `set -x` in the gateway entrypoint, printing every shell command to the container log. Noisy but useful for diagnosing boot failures.

* **`OPENCLAW_PROVIDER_PLUGINS`** — **Optional**. Default `openai`. Space-separated list of OpenClaw provider plugin names to install at gateway boot. OpenClaw 6.x ships provider integrations as installable plugins rather than bundling them — without the provider plugin, `openclaw models auth` fails and agents cannot resolve their model. The entrypoint installs any plugins in this list that aren't already installed. Override to add additional providers (e.g. `openai anthropic google`).

* **`OPENCLAW_VERSION`** — **Optional**. Default unset. Informational version string written into the `gateways.meta.version` field during registration. Used by the UI to display the gateway's OpenClaw version. Typically set at image build time.

### 2.10 Image overrides

Pin specific image tags for reproducible production deploys. All four default to `:latest`.

* **`UI_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-ui:latest`.
* **`GATEWAY_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-gateway:latest`.
* **`DISPATCHER_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-dispatcher:latest`.
* **`RUNNER_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-runner:latest`.
* **`EMBEDDER_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-embedder:latest`.
* **`FILE_PROCESSOR_IMAGE`** — **Optional**. Default `ghcr.io/yourhq/yourhq-file-processor:latest`.

Override to `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.11 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 from `localhost: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 with `CODESPACE_NAME` by 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 bake `NEXT_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:latest` image works for every user. No `docker compose build ui` required — ever.
* Changing Supabase creds is a UI action (Settings → Database → Rotate), not a rebuild.
* `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` env vars are no longer read by the UI. Leave them unset.

***

### 2.12 Secrets encryption

These variables control how user-managed secrets (channel tokens, API keys, OAuth tokens) are encrypted in the `secrets` 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**. Default `60`. Seconds between source-sync poll cycles (checks `source_connections` for connections due for sync). Not secrets-specific but relevant because source sync now reads OAuth tokens from the secrets filesystem.

Self-hosted deployments do not need `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.13 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 on `DEPLOYMENT_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 with `NEXT_PUBLIC_` because these runtimes don't need Next.js client bundling.
* **`SENTRY_AUTH_TOKEN`** — **Hosted only, build-time (Vercel)**. A Sentry personal API token with `project:releases` and `org:read` scopes. Used by `@sentry/nextjs` during 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.

Gateway daemons (Python) use `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**. Default `https://us.i.posthog.com`. PostHog ingest endpoint.
* **`POSTHOG_API_KEY`** — **Hosted only**. Empty. Server-side PostHog API key for the worker.

Like Sentry, PostHog is gated on `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](/guides/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 `.env` if you use env overrides. Restart gateway services after changing env overrides. Audit `audit_log` for suspicious activity.

* **UI anon key** (stored in `/config/workspaces.json` per 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 running `docker 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`, then `docker compose up -d gateway`. The gateway recreates `~/.ssh/openclaw_deploy_key` if it's missing, but **it doesn't overwrite an existing one** — you may need to `docker compose exec gateway rm /home/openclaw/.ssh/openclaw_deploy_key` first.

* **`GITHUB_TOKEN`** — rotate the PAT on GitHub (or revoke it), update `.env`, then `docker compose up -d gateway` so 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`, then `docker 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 run `tailscale up` on 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:

```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
```

**No environment variables change between production and development** — the dev overlay only changes volume mounts, commands, and the UI image (`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](/concepts/agents) for agent-specific configuration (each agent has its own `agent.json` committed to its git branch).

Rule of thumb: if it varies between workspaces of the same deployment, it's probably in the DB and configured in the UI. If it's infrastructure-level (where Supabase lives, how the host is networked, which git remote to back up to), it's in `.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`:

```bash theme={null}
docker compose up -d ui
```

### UI workspace config (no rebuild required)

Supabase URL, anon key, service role key per workspace live in the workspace registry on the `ui-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`:

```bash theme={null}
docker compose up -d gateway
```

Note: `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`:

```bash theme={null}
docker compose up -d dispatcher runner
```

### 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:

```bash theme={null}
docker compose up -d --force-recreate
```

### 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`:

```bash theme={null}
docker compose pull && docker compose up -d
```

### 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:

```bash theme={null}
docker compose up -d gateway dispatcher runner
```

***

## 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_TOKEN` differs between UI and gateway, or is empty. Set it identically in `.env` and run `docker compose up -d ui gateway`.
* **"Changed the port but the old one still works"** — port bindings need `--force-recreate` (section 7).
* **"Changed `TEMPLATES_SOURCE` but 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_URL` is written into Supabase by the gateway at startup; fix `.env` and restart the gateway, not the UI.
