Beginner
macOS / Linux / Windows (WSL2) / Docker
Estimated time: 35 min

OpenClaw Configuration Guide: openclaw.json, Models, Gateway, Channels, and Plugins

Set up openclaw.json without guesswork: confirm the active config path, choose a safe baseline, add providers and channels in the right order, and verify each change before it becomes a production problem.

Implementation Steps

Start by locating your state directory and openclaw.json, and learn what overwrites what (CLI vs file vs env).

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:

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:

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.json directly,
  • 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:

  1. the effective state directory and config path the running gateway is using,
  2. whether the gateway is launched from your shell, a service manager, or a container,
  3. which provider/model path you want to make work first,
  4. which gateway bind/auth posture you actually intend to run,
  5. 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_DIR changes where the entire state (sessions, creds, caches) lives.
  • OPENCLAW_CONFIG_PATH points 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:

  1. Probe model resolution:
openclaw models status --probe
  1. 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:

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).
  • .env in your current working directory (if present).
  • ~/.openclaw/.env as 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): model only (example: qwen2.5:7b)

In practice:

  • Use refs in agents.defaults.model.primary and 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.1 over localhost to avoid IPv6 ::1 edge 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 curl fails and openclaw models status --probe fails, you are still in config/auth/network territory.
  • If curl works but openclaw models status --probe fails, re-check the actual OpenClaw config, runtime environment, and resolved provider.
  • If curl works and openclaw models status --probe works, but openclaw agent or 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:

  • tools
  • tool_choice
  • parallel_tool_calls
  • reasoning_effort
  • store
  • stream

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:

  1. You put secrets in the wrong place.
    • auth.profiles in openclaw.json is metadata + routing, not where keys are stored.
    • Provider auth is resolved in this order: auth-profiles.json → env vars → models.providers.*.apiKey.
  2. Your service environment doesn’t have the same env vars as your shell.
    • A key that works in curl from your terminal can still be missing for launchd/systemd/Docker.

Fast recovery checklist:

  1. Confirm the gateway can authenticate to the provider:
openclaw models status --probe
  1. 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" },
      },
    },
  },
}
  1. 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 messages payload

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 doctor or your normal validation path no longer shows config/schema problems,
  • openclaw gateway status or equivalent runtime checks point at the instance you intended to edit,
  • openclaw models status --probe proves 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)

SymptomUsually meansFirst things to tryDeep link
Gateway won’t start after editing configJSON5 parse error or strict validation erroropenclaw doctor then openclaw doctor --fix/troubleshooting/solutions/config-validation-failed/
Gateway refuses to bind without authSafety check: binding to non-loopback requires authConfigure 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 browserRun openclaw dashboard and use the tokenized URL/troubleshooting/solutions/control-ui-unauthorized/
Gateway is running but probes failCLI is probing the wrong instance / remote mode mismatchCheck gateway URL/mode, then re-probe/troubleshooting/solutions/gateway-service-running-but-probe-fails/
/model says “model not allowed”agents.defaults.models allowlist blocks selectionopenclaw config get agents.defaults.models/troubleshooting/solutions/model-not-allowed/
Ollama configured but runtime still falls backEndpoint unreachable from gateway runtime, or model catalog mismatchProbe: openclaw models status --probe/troubleshooting/solutions/ollama-configured-but-falls-back-to-anthropic/
Docker shows openclaw.json.*.tmp EACCESBind-mounted state dir isn’t writable by container uidFix uid/gid or mount ownership/troubleshooting/solutions/docker-eacces-openclaw-config-dir/
OAuth renewal succeeded but recovery still loopsLive auth updated, but recovery still trusts stale provisioned auth/safe-mode stateResync auth files, clear stale safe-mode markers, then probe again/guides/safe-mode-recovery-after-oauth-renewal
Linux host keeps spawning duplicate gatewaysCLI respawn path is colliding with a systemd-managed gatewayRestart through systemctl --user and set OPENCLAW_NO_RESPAWN=1/troubleshooting/solutions/linux-systemd-orphan-gateway-processes
Telegram bot “connects” but nothing arrivesBot not in group / privacy mode / mis-set group policyFollow Telegram guide + validate updates/guides/telegram-setup
You want a safe config without hand-editing JSONYou need a schema-aligned baselineUse the generator, then iterate with CLI edits/openclaw-config-generator

AI API compatibility guides

More configuration-heavy guides

Verification & references

  • Reviewed by:CoClaw Editorial Team
  • Last reviewed:March 14, 2026
  • Verified on: macOS · Linux · Windows (WSL2) · Docker

Related Resources

OpenClaw Starter Config Presets: 3 Safe Baselines You Can Actually Run
Guide
Choose a safe OpenClaw starter preset for local, coding, or remote use, install it with a rollback path, and verify the baseline before you add channels, skills, or automation.
How to Choose Between Native Ollama, OpenAI-Compatible /v1, vLLM, and LiteLLM for OpenClaw
Guide
Choose the right OpenClaw model-serving path, validate the first backend cleanly, and know what tradeoffs you are accepting before you add tools, routing, or proxy layers.
OpenClaw Safe Mode Recovery After OAuth Renewal: auth-profiles.json vs provisioned copy
Guide
Understand why OpenClaw recovery can keep looping after a successful OAuth renewal, and how to resync the live auth file, the provisioned copy, and safe-mode markers.
Gateway won't start: 'refusing to bind ... without auth'
Fix
Fix gateway startup failures when binding to LAN/tailnet without token/password auth configured.
OpenClaw config becomes invalid after you remove a provider but keep its auth profile
Fix
Fix config validation failures caused by orphaned `auth.profiles` entries that still reference a provider removed from `models.providers`.
Docker: EACCES permission denied writing ~/.openclaw (openclaw.json.*.tmp)
Fix
Fix Docker permission errors when the gateway container can't write to the bind-mounted ~/.openclaw directory (common when host UID/GID != 1000).
OpenClaw Quick Start: First Success in 5 Minutes
Start
A beginner-safe path to install OpenClaw, run onboarding, and confirm the local dashboard works end to end.

Need live assistance?

Ask in the community forum or Discord support channels.

Get Support