This guide is a âmapâ of OpenClaw configuration. It is not a full schema reference, but it should get you from âI have a config fileâ to âI can change behavior confidently and debug problems quickly.â
If you are brand new, start here first:
- /guides/new-user-checklist
- If youâre currently stuck installing OpenClaw, use: /guides/openclaw-installation-troubleshooting
If you want a safe starting point without hand-editing JSON, use:
If you are currently blocked by an error, jump to:
If your problem is specifically a Windows runtime / service / PATH mismatch, start with:
- OpenClaw on Native Windows: PATH, Scheduled Tasks, Node Host, and the Real Failure Modes
- Windows: tools.exec cannot find docker, rg, or gh even though they work in PowerShell
What this guide helps you finish
By the end of this guide, you should be able to:
- identify which config file and state directory your running gateway is actually using,
- choose a safe baseline for gateway auth, models, and channels,
- make one meaningful config change at a time without losing track of precedence,
- verify that the change worked before you keep layering complexity on top.
Who this is for (and not for)
Use this guide if you are:
- editing
openclaw.jsondirectly, - moving from a starter setup into a more deliberate provider/channel layout,
- debugging a config that âlooks rightâ but behaves differently at runtime.
This is not the best first stop if you only need a quick preset or are still blocked on installation itself. In those cases, start with the generator, the starter presets guide, or installation troubleshooting first.
Before you edit config: collect these five facts
Before you change anything, confirm:
- the effective state directory and config path the running gateway is using,
- whether the gateway is launched from your shell, a service manager, or a container,
- which provider/model path you want to make work first,
- which gateway bind/auth posture you actually intend to run,
- which channel or plugin change is worth testing after the baseline works.
If you cannot answer those five questions yet, you are still in discovery mode. Slow down and gather them first.
0) Where configuration lives
OpenClaw is stateful. Most users should treat the state directory as âthe installâ:
- Default state directory:
~/.openclaw/ - Default config file:
~/.openclaw/openclaw.json
If you are in Docker/CI/remote hosts, the state directory may be overridden (for example via an environment variable or a container volume). When debugging, always confirm you are editing the same config that the gateway is reading.
Config file path overrides (the 60-second mental model)
When something âignoresâ your config, it is often because you are editing the wrong file.
Common overrides:
OPENCLAW_STATE_DIRchanges where the entire state (sessions, creds, caches) lives.OPENCLAW_CONFIG_PATHpoints the gateway at a specific config file (can be outside the state dir).
Practical rule: if you are running OpenClaw via a service manager (launchd/systemd) or Docker, the runtime environment
may not match your interactive shell. Confirm the service is using the same OPENCLAW_CONFIG_PATH / OPENCLAW_STATE_DIR
you think it is.
On native Windows, this same mismatch often shows up as âPowerShell can find the tool, but the gateway service cannot.â For that path specifically, see:
Tip: you can run multiple isolated gateways on one machine by giving each instance its own state + config:
OPENCLAW_STATE_DIR=~/.openclaw-main OPENCLAW_CONFIG_PATH=~/.openclaw-main/openclaw.json openclaw gateway --port 18789
OPENCLAW_STATE_DIR=~/.openclaw-lab OPENCLAW_CONFIG_PATH=~/.openclaw-lab/openclaw.json openclaw gateway --port 19001
1) Three ways to change config (and when to use each)
A) Config generator (best for structure)
The generator helps you avoid schema/indentation mistakes and keeps you from accidentally enabling unsafe defaults:
B) CLI config commands (best for small edits)
For targeted changes, prefer CLI writes (they tend to preserve structure better than manual edits):
openclaw config get agents.defaults.model.primary
openclaw config set gateway.bind loopback
C) Editing openclaw.json (best for âversionableâ config)
This is the best option if you want to store config in git, review diffs, and make grouped changes.
Important: OpenClaw allows JSON5-style syntax in many places (comments, trailing commas). If you use strict JSON tools, you may accidentally break formatting or remove comments.
1.5) Safe editing and recovery (avoid âself-brickingâ)
Community reports show the same pattern repeatedly: a small âjust switch model / enable pluginâ change turns into a gateway
that wonât start because openclaw.json is now invalid (syntax), schema-invalid (wrong keys/types), or references a model
id your provider doesnât support.
Use this workflow to make config changes recoverable.
A) Always keep a rollback copy
Before any manual edits:
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak.$(date +%Y%m%d-%H%M%S)
If youâre iterating fast, also keep a âknown-good minimalâ file you can swap in.
B) Prefer CLI edits for small changes
If you are only changing one field, prefer openclaw config set over hand-editing:
openclaw config get agents.defaults.model.primary
openclaw config set agents.defaults.model.primary openrouter/auto
This reduces the chance of JSON5 syntax errors and tends to preserve structure.
C) Validate after every meaningful change
If the gateway wonât start (or you suspect config drift), run:
openclaw doctor
If it offers a safe migration/fix:
openclaw doctor --fix
If you see Failed to parse / validation / Unknown key / expected number, received string, use the dedicated fix page:
D) If you changed models and now OpenClaw is âbrokenâ
Two quick checks that usually isolate the issue:
- Probe model resolution:
openclaw models status --probe
- Revert the last known-good config:
ls -1 ~/.openclaw/openclaw.json.bak.* 2>/dev/null | tail -n 5
Then copy one back into place and restart the gateway.
If you are unsure which config file the gateway is reading (service vs shell), re-check:
2) Secrets and modular config (recommended once your config grows)
Use env var substitution for secrets
Avoid committing tokens and API keys into openclaw.json. Prefer env var substitution:
{
gateway: { auth: { mode: "token", token: "${OPENCLAW_GATEWAY_TOKEN}" } },
models: {
providers: {
openrouter: { apiKey: "${OPENROUTER_API_KEY}" },
},
},
}
Notes:
- Only uppercase env var names are substituted (
[A-Z_][A-Z0-9_]*). - Missing or empty values typically fail at load time (good: fail fast).
- To output a literal
${VAR}string, escape like$${VAR}.
Where env vars can come from (common defaults):
- The gateway parent process environment (launchd/systemd/Docker environment is what matters).
.envin your current working directory (if present).~/.openclaw/.envas a global fallback.
If you consistently see âtoken missingâ or providers look unauthenticated only when running as a service, you are debugging environment propagation, not JSON syntax.
Optional (advanced): if you do not want to hardcode env vars into a service definition, OpenClaw can import missing env vars from your login shell. This can help on macOS/Linux when your shell profile exports API keys, but your service environment does not.
{
env: {
shellEnv: { enabled: true, timeoutMs: 15000 },
},
}
Trade-off: shell env import can hide where secrets come from. Prefer explicit service env vars for production.
Split large configs with $include
Once your config contains multiple channels, multiple agents, and custom models, use $include to keep it readable.
Example:
// ~/.openclaw/openclaw.json
{
gateway: { $include: "./gateway.json5" },
agents: { $include: "./agents.json5" },
models: { $include: "./models.json5" },
channels: { $include: "./channels.json5" },
}
Good to know:
$include: "./file.json5"replaces the containing object.$include: ["./a.json5", "./b.json5"]deep-merges in order (later wins).- Sibling keys are applied after includes (so you can override included defaults).
- Relative paths resolve relative to the including file.
- Nested includes are supported up to 10 levels deep.
Security note: $include is great for keeping secrets out of the main file (for example, a local secrets.json5
that is excluded by gitignore), but you still need to protect the included file at rest.
3) Minimal safe baseline (gateway first, then channels)
A common beginner failure mode is: âIt works on localhost, then I expose it and accidentally run an open relay.â
Start with a conservative gateway:
{
gateway: {
mode: "local",
bind: "loopback", // only local machine
auth: { mode: "token", token: "${OPENCLAW_GATEWAY_TOKEN}" },
},
}
Notes:
- Prefer token auth even locally. It avoids âunauthorizedâ surprises in the Control UI and makes habits portable.
- If you use env substitution (
${...}), make sure your runtime (launchd/systemd/docker) actually exports that env var.
4) Gateway reload behavior (why some changes âneed a restartâ)
Most config changes hot-apply, but gateway server settings usually require a restart (bind/port/auth/TLS/etc).
If you want predictable reload behavior, set an explicit reload mode:
{
gateway: {
reload: { mode: "hybrid", debounceMs: 300 }, // hot | hybrid | restart | off
},
}
If you edit config and nothing changes, confirm:
- you edited the active config file (see Config file path overrides), and
- the gateway reload mode isnât set to
off, and - your change is in a category that hot-applies.
If you need a quick âit must pick up changesâ workflow, use:
openclaw gateway restart
5) Models & providers: the mental model that prevents 50% of bugs
Model ârefsâ vs provider catalog âidsâ
You will see two similar-looking strings:
- Model ref (selection):
provider/model(example:ollama/qwen2.5:7b) - Model id (catalog entry):
modelonly (example:qwen2.5:7b)
In practice:
- Use refs in
agents.defaults.model.primaryand when selecting with CLI. - Use raw ids in
models.providers.<provider>.models[].id.
Extra gotcha: when you run openclaw models status --probe with --agent <id>, it uses the agentâs model/auth state.
When omitted, the CLI may use OPENCLAW_AGENT_DIR / PI_CODING_AGENT_DIR if set (common in multi-agent setups).
A correct Ollama config example
{
models: {
mode: "merge",
providers: {
ollama: {
// Prefer 127.0.0.1 to avoid IPv6 localhost (::1) edge cases.
baseUrl: "http://127.0.0.1:11434/v1",
api: "openai-completions",
models: [{ id: "qwen2.5:7b", name: "qwen2.5:7b" }],
},
},
},
agents: {
defaults: {
model: { primary: "ollama/qwen2.5:7b" },
},
},
}
If Ollama is configured but requests still fall back or silently fail, see:
Verify model selection the right way
Before you test in the UI, confirm what the runtime actually resolved:
openclaw models status --probe
If you see a different provider than expected, you are debugging resolution (config/catalog), not generation quality.
About models.mode (merge vs replace)
When you add models.providers, you can choose how it combines with the built-in/implicit catalog:
merge(common): add/override providers and keep everything else available.replace(strict): only the providers you list are available.
If you are building a locked-down install for a team, replace can reduce accidental provider drift. If you are a new
user, merge is usually less surprising.
Two âgotchaâ checks for local endpoints
If you point a provider at a local process (Ollama, LM Studio, vLLM, LiteLLM, etc.):
- Prefer
127.0.0.1overlocalhostto avoid IPv6::1edge cases. - Make sure the gateway runtime is the same network namespace as the local process (containers and WSL can break this).
A safe starting point for custom OpenAI-compatible providers
If you are connecting OpenClaw to a custom endpoint that is described only as âOpenAI-compatibleâ, start conservative.
Many endpoints support basic chat completions but do not fully support:
- tools,
- tool-result continuation,
- reasoning controls,
- or streaming behavior in the same way OpenClaw expects.
Safe first-pass example:
{
models: {
mode: "merge",
providers: {
localqwen: {
baseUrl: "http://127.0.0.1:2005/v1",
apiKey: "${LOCALQWEN_API_KEY}",
api: "openai-completions",
models: [
{
id: "DeepSeek-R1-Distill-Qwen-32B",
name: "DeepSeek-R1-Distill-Qwen-32B",
reasoning: false,
input: ["text"],
compat: {
supportsTools: false,
},
},
],
},
},
},
agents: {
defaults: {
model: { primary: "localqwen/DeepSeek-R1-Distill-Qwen-32B" },
},
},
}
This is a good first test because it answers a narrow question:
- can this endpoint survive a real OpenClaw run as a plain chat backend?
If yes, you can add more advanced compatibility back gradually.
curl success vs agent success: how to tell config bugs from payload bugs
Use this quick decision rule:
- If
curlfails andopenclaw models status --probefails, you are still in config/auth/network territory. - If
curlworks butopenclaw models status --probefails, re-check the actual OpenClaw config, runtime environment, and resolved provider. - If
curlworks andopenclaw models status --probeworks, butopenclaw agentor TUI fails, you are probably in runtime payload compatibility territory.
That last case is increasingly common with custom OpenAI-compatible endpoints. The backend may accept a minimal chat request but reject fields such as:
toolstool_choiceparallel_tool_callsreasoning_effortstorestream
If that matches your symptom pattern, use:
6) Common config pitfalls (and how to avoid them)
âConfig validation failedâ after editing
Usually means:
- A key is misspelled or moved between versions.
- JSON/JSON5 parse error (missing comma, unclosed quote).
Fix flow:
âModel not allowedâ (allowlist confusion)
If agents.defaults.models is non-empty, it becomes an allowlist.
Fix flow:
Token/auth issues that look like âno responseâ
Two common patterns:
- Control UI is âunauthorizedâ because you opened a non-tokenized URL (use
openclaw dashboard). - Gateway token isnât being read in the runtime environment (launchd/systemd/docker env mismatch).
âChat works, but tools fail with âNo API keyââ (auth profiles vs provider apiKey)
This is a surprisingly common failure mode with custom providers and OpenAI-compatible endpoints:
- basic chat requests succeed,
- but a real agent run fails once tools are involved (or you see âmissing key / unauthenticatedâ only in certain flows).
The most common root causes:
- You put secrets in the wrong place.
auth.profilesinopenclaw.jsonis metadata + routing, not where keys are stored.- Provider auth is resolved in this order:
auth-profiles.jsonâ env vars âmodels.providers.*.apiKey.
- Your service environment doesnât have the same env vars as your shell.
- A key that works in
curlfrom your terminal can still be missing for launchd/systemd/Docker.
- A key that works in
Fast recovery checklist:
- Confirm the gateway can authenticate to the provider:
openclaw models status --probe
- Put the API key on the provider entry (env-substitution is the safest default):
{
models: {
providers: {
myprovider: {
apiKey: "${MYPROVIDER_API_KEY}",
},
},
},
}
If youâre using SecretRefs, this shape is valid too:
{
models: {
providers: {
myprovider: {
apiKey: { source: "env", provider: "default", id: "MYPROVIDER_API_KEY" },
},
},
},
}
- Restart and retest:
openclaw gateway restart
openclaw models status --probe
If your provider works for chat but fails when tools are enabled, also verify that the provider/model catalog entry is honest about tool support (compat.supportsTools), and start with tools disabled until you confirm compatibility.
Custom provider works in curl, but still fails in OpenClaw
This usually means you have already solved the endpoint problem and are now hitting a payload compatibility problem.
The most common mistake is assuming that âOpenAI-compatibleâ means âsupports everything OpenClaw may send in a real runâ. It often only means:
- basic
/v1/chat/completions - basic auth headers
- basic
messagespayload
It does not automatically mean support for tools, tool-result continuation, reasoning controls, or provider-specific streaming behavior.
Docker permission errors writing config/state
If your container canât write temporary config files under the mounted state directory:
7) Debugging: the small command set to memorize
# What config is the gateway using?
openclaw config get gateway
# Is the gateway running and reachable?
openclaw gateway status
# Which model/provider will actually be used?
openclaw models status --probe
# Follow logs while reproducing an issue
openclaw logs --follow
When you share logs publicly, redact:
- gateway tokens
- API keys
- OAuth tokens
Verification checklist after any meaningful config change
Before you call a config change âdone,â verify all four of these:
openclaw doctoror your normal validation path no longer shows config/schema problems,openclaw gateway statusor equivalent runtime checks point at the instance you intended to edit,openclaw models status --probeproves the provider/model path you meant to change is really live,- the first affected workflow (dashboard, channel, plugin, or model route) behaves correctly twice in a row, not just once.
That last point matters. One successful test often proves only that the happy path exists. Two consecutive successful tests are a much better signal that precedence, auth, and state all line up.
8) Fast FAQ (symptom -> fix)
| Symptom | Usually means | First things to try | Deep link |
|---|---|---|---|
| Gateway wonât start after editing config | JSON5 parse error or strict validation error | openclaw doctor then openclaw doctor --fix | /troubleshooting/solutions/config-validation-failed/ |
| Gateway refuses to bind without auth | Safety check: binding to non-loopback requires auth | Configure gateway.auth then restart | /troubleshooting/solutions/gateway-refusing-to-bind-without-auth/ |
| Control UI says âunauthorizedâ | You opened a non-tokenized URL or token isnât set in the browser | Run openclaw dashboard and use the tokenized URL | /troubleshooting/solutions/control-ui-unauthorized/ |
| Gateway is running but probes fail | CLI is probing the wrong instance / remote mode mismatch | Check gateway URL/mode, then re-probe | /troubleshooting/solutions/gateway-service-running-but-probe-fails/ |
/model says âmodel not allowedâ | agents.defaults.models allowlist blocks selection | openclaw config get agents.defaults.models | /troubleshooting/solutions/model-not-allowed/ |
| Ollama configured but runtime still falls back | Endpoint unreachable from gateway runtime, or model catalog mismatch | Probe: openclaw models status --probe | /troubleshooting/solutions/ollama-configured-but-falls-back-to-anthropic/ |
Docker shows openclaw.json.*.tmp EACCES | Bind-mounted state dir isnât writable by container uid | Fix uid/gid or mount ownership | /troubleshooting/solutions/docker-eacces-openclaw-config-dir/ |
| OAuth renewal succeeded but recovery still loops | Live auth updated, but recovery still trusts stale provisioned auth/safe-mode state | Resync auth files, clear stale safe-mode markers, then probe again | /guides/safe-mode-recovery-after-oauth-renewal |
| Linux host keeps spawning duplicate gateways | CLI respawn path is colliding with a systemd-managed gateway | Restart through systemctl --user and set OPENCLAW_NO_RESPAWN=1 | /troubleshooting/solutions/linux-systemd-orphan-gateway-processes |
| Telegram bot âconnectsâ but nothing arrives | Bot not in group / privacy mode / mis-set group policy | Follow Telegram guide + validate updates | /guides/telegram-setup |
| You want a safe config without hand-editing JSON | You need a schema-aligned baseline | Use the generator, then iterate with CLI edits | /openclaw-config-generator |
Troubleshooting quick links
- Gateway wonât start / validation errors:
- Ollama configured but still falls back:
- Model not allowed:
- Telegram setup:
- OAuth recovery loops after renewal:
- Linux/systemd duplicate gateway processes:
- WhatsApp setup:
AI API compatibility guides
- Compatibility matrix: /guides/self-hosted-ai-api-compatibility-matrix
- Choosing a local or proxy API path: /guides/choose-local-ai-api-path-for-openclaw
- Relay and proxy troubleshooting: /guides/openclaw-relay-and-api-proxy-troubleshooting
More configuration-heavy guides
- Docker deployment: /guides/docker-deployment
- Updating/migration: /guides/updating-and-migration