Why this guide exists
Out of the box, if you deploy OpenClaw without an OpenAI API key, the gateway starts but immediately crashes with:
[WARN] No primary model found
Error: No API key found for provider "anthropic"
Auth store: /data/.openclaw/agents/main/agent/auth-profiles.jsonThis happens because of a cascade:
openrouter/ prefix. Either alone is not enough.Remove any host-level OpenClaw install
OpenClaw should only live inside the Docker container. If it's also installed on the host OS, commands like openclaw gateway status will talk to the wrong environment and silently cause confusion.
Check if it's on the host:
which openclawIf it returns a path, remove it:
# If installed via npm
npm uninstall -g openclaw
# If it's a binary (adjust path to match `which openclaw` output)
rm /usr/local/bin/openclaw
# Clean up any host-level config
rm -rf ~/.openclawVerify it's gone. This should return nothing:
which openclawopenclaw command must be prefixed with docker compose exec openclaw openclaw.Verify the container is running
Navigate to your Compose project directory and check the container status:
cd /docker/openclaw-qrjy # adjust to match your deployment folder
docker compose psYou should see the openclaw service with status Up. If not:
docker compose up -d
docker compose logs -fInject the OpenRouter API key
First confirm whether the key is already visible inside the container:
docker compose exec openclaw printenv OPENROUTER_API_KEYIf the output is empty, add it to the environment: block in your docker-compose.yml:
services:
openclaw:
environment:
- OPENROUTER_API_KEY=sk-or-YOUR_KEY_HEREThen restart so the container picks it up:
docker compose up -dConfigure OpenRouter auth
Run the onboarding command to write the auth file the gateway reads at startup:
docker compose exec openclaw openclaw onboard \
--auth-choice apiKey \
--token-provider openrouter \
--token "$OPENROUTER_API_KEY"Verify the auth file was written correctly:
docker compose exec openclaw cat /data/.openclaw/agents/main/agent/auth-profiles.jsonA healthy auth file contains openrouter and your key, along with usage stats like:
"usageStats": {
"openrouter:default": {
"lastUsed": 1771685494768,
"errorCount": 0
}
}errorCount: 0 confirms OpenRouter is accepting the key. If you see errors here, double-check the key starts with sk-or-.Set the model to use the openrouter/ prefix
The default logs may show agent model: anthropic/claude-sonnet-4. a direct Anthropic reference that bypasses OpenRouter entirely and causes the auth failure. Every model ref must carry the openrouter/ prefix.
Use openclaw config set to update the primary model:
docker compose exec openclaw openclaw config set \
agents.defaults.model.primary "openrouter/anthropic/claude-sonnet-4-5"[INFO] Writing OpenClaw config). However, OPENCLAW_MODEL does not exist in this image. The model is stored in the volume at /data/.openclaw/openclaw.json, which persists across restarts. Using openclaw config set writes to this file correctly.Other valid model references:
openrouter/openai/gpt-4o-miniFast, cheap, great defaultopenrouter/anthropic/claude-sonnet-4-5Strong default for most tasksopenrouter/deepseek/deepseek-r1Strong reasoning, very low costAfter setting the model, restart and watch the logs:
docker compose restart
docker compose logs -fConfirm the logs now show agent model: openrouter/anthropic/... and no more No API key found for provider "anthropic" errors.
Pair Telegram
The Telegram bot token is already configured in Docker. Pairing just links your personal Telegram account to the bot.
Trigger a pairing request
Open Telegram and send any message (e.g. hi) to your bot. It will reply with a pairing code like APGRJ2MS.
List pending requests
docker compose exec openclaw openclaw pairing list telegramApprove the pairing
docker compose exec openclaw openclaw pairing approve telegram APGRJ2MSpairing pending (unknown command, use pairing list telegram instead), and omitting telegram (gives "Channel required" error). Always pass the channel.Why pairing kept resetting
If the gateway crashes on every message (because OpenRouter auth wasn't configured), each crash loses the pairing state. Fix Steps 4–6 first, then pair. Once the gateway is stable, pairing sticks permanently.
Device pairing: the chicken-and-egg fix
OpenClaw has a separate device pairing layer for the Control UI and CLI. On a fresh VPS install you may hit this error even from inside the container:
gateway connect failed: Error: pairing required
Gateway target: ws://127.0.0.1:18789
Source: local loopbackThe CLI can't connect to approve itself as a device. Because it isn't approved yet. To break the loop, edit the config directly inside the container:
docker compose exec openclaw /bin/bashInside the container, inspect the config:
cat /data/.openclaw/openclaw.jsonLook for devicePairing or requireDevicePairing. Disable it or add a trusted device entry, then exit and restart:
exit
docker compose restartAfter restart, approve the device normally:
docker compose exec openclaw openclaw devices list
docker compose exec openclaw openclaw devices approve <requestId>Command reference
Every command you'll need, in one place. All run from the Compose project directory.
docker compose logs -fdocker compose restartdocker compose exec openclaw /bin/bashdocker compose exec openclaw openclaw gateway statusdocker compose exec openclaw openclaw onboard --auth-choice apiKey --token-provider openrouter --token "$OPENROUTER_API_KEY"docker compose exec openclaw openclaw config set agents.defaults.model.primary "openrouter/anthropic/claude-sonnet-4-5"docker compose exec openclaw openclaw pairing list telegramdocker compose exec openclaw openclaw pairing approve telegram <CODE>docker compose exec openclaw openclaw devices listdocker compose exec openclaw openclaw devices approve <requestId>Key lessons
Troubleshooting
Post-mortem: how we broke it trying to optimise it
After getting everything working, we tried applying the multi-model routing config from the blog post. And broke the gateway completely. Here's the exact failure chain and what fixed it.
What went wrong
The fix: two commands
The auth file (auth-profiles.json) was correct throughout. Only the config file ownership was broken. Fixing that was enough:
# Restore ownership of the config file
docker compose exec --user root openclaw chown openclaw:openclaw /data/.openclaw/openclaw.json
# Restart so the gateway re-reads it cleanly
docker compose restart
docker compose logs -fEverything running
Remove any host install, inject the key into Compose, run onboard inside the container, set the openrouter/ model ref, restart. Once the gateway is stable, Telegram pairing sticks and every model on OpenRouter is available.