Guide

OpenClaw + OpenRouter: Full Setup

A complete walkthrough for a Hostinger VPS deployment: from a blank Docker container to a working OpenClaw instance routing through OpenRouter with Telegram paired. Every gotcha included.

10 min readFebruary 2026
Prerequisites
Hostinger VPS with OpenClaw deployed via the one-click Docker template
SSH access to the VPS (root@<your-vps-ip>)
An OpenRouter API key (sk-or-...) from openrouter.ai
A Telegram bot token from @BotFather
01

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.json

This happens because of a cascade:

01
No primary model configured
OpenClaw falls back to a default anthropic/claude-* reference.
02
That ref calls Anthropic directly
Not via OpenRouter. It tries to hit the Anthropic API with no key.
03
Every message fails, gateway restarts
And because the gateway crashes, Telegram pairing resets too.
The fix is two things done together: configure OpenRouter auth AND set the model with the openrouter/ prefix. Either alone is not enough.
02

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 openclaw

If 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 ~/.openclaw

Verify it's gone. This should return nothing:

which openclaw
From this point on, every openclaw command must be prefixed with docker compose exec openclaw openclaw.
03

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 ps

You should see the openclaw service with status Up. If not:

docker compose up -d
docker compose logs -f
04

Inject the OpenRouter API key

First confirm whether the key is already visible inside the container:

docker compose exec openclaw printenv OPENROUTER_API_KEY

If the output is empty, add it to the environment: block in your docker-compose.yml:

docker-compose.yml
services:
  openclaw:
    environment:
      - OPENROUTER_API_KEY=sk-or-YOUR_KEY_HERE

Then restart so the container picks it up:

docker compose up -d
Putting the key in the Compose file means it survives reboots and container recreations automatically. Unlike a shell export which disappears on restart.
05

Configure 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.json

A 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-.
06

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"
The Hostinger entrypoint rewrites config from environment variables on every container start ([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 default
openrouter/anthropic/claude-sonnet-4-5Strong default for most tasks
openrouter/deepseek/deepseek-r1Strong reasoning, very low cost

After setting the model, restart and watch the logs:

docker compose restart
docker compose logs -f

Confirm the logs now show agent model: openrouter/anthropic/... and no more No API key found for provider "anthropic" errors.

07

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 telegram

Approve the pairing

docker compose exec openclaw openclaw pairing approve telegram APGRJ2MS
Two common mistakes: using pairing 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.

08

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 loopback

The 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/bash

Inside the container, inspect the config:

cat /data/.openclaw/openclaw.json

Look for devicePairing or requireDevicePairing. Disable it or add a trusted device entry, then exit and restart:

exit
docker compose restart

After restart, approve the device normally:

docker compose exec openclaw openclaw devices list
docker compose exec openclaw openclaw devices approve <requestId>
09

Command reference

Every command you'll need, in one place. All run from the Compose project directory.

Follow container logs
docker compose logs -f
Restart container
docker compose restart
Shell inside container
docker compose exec openclaw /bin/bash
Check gateway status
docker compose exec openclaw openclaw gateway status
Run onboarding
docker compose exec openclaw openclaw onboard --auth-choice apiKey --token-provider openrouter --token "$OPENROUTER_API_KEY"
Set default model
docker compose exec openclaw openclaw config set agents.defaults.model.primary "openrouter/anthropic/claude-sonnet-4-5"
List Telegram pairings
docker compose exec openclaw openclaw pairing list telegram
Approve Telegram pairing
docker compose exec openclaw openclaw pairing approve telegram <CODE>
List devices
docker compose exec openclaw openclaw devices list
Approve device
docker compose exec openclaw openclaw devices approve <requestId>
10

Key lessons

01
Never run openclaw directly on the host
OpenClaw lives inside the container. All commands must go through docker compose exec openclaw openclaw.
02
Set the model first
[WARN] No primary model found on startup causes OpenClaw to fall back to a direct anthropic/ ref. Always set an openrouter/ prefixed model before anything else.
03
OpenRouter auth goes in the container
The auth file that matters is /data/.openclaw/agents/main/agent/auth-profiles.json inside the container, not anything on the host.
04
Telegram pairing resets if the gateway crashes
Fix the AI provider auth first. Once the gateway is stable, pairing is permanent.
05
There are two separate pairing systems
openclaw pairing links Telegram/WhatsApp user accounts. openclaw devices links CLI/browser clients to the gateway Control UI. They're independent.
06
Never edit volume files as root
Running docker compose exec without --user runs as root on some systems. If you write openclaw.json as root, the openclaw process can't read it and every operation fails with EACCES. Fix with: docker compose exec --user root openclaw chown openclaw:openclaw /data/.openclaw/openclaw.json
11

Troubleshooting

"No API key found for provider anthropic"
OpenRouter auth not configured. Run Step 05 (onboarding).
"No primary model found" on startup
Set the model with the openrouter/ prefix. Run Step 06.
Telegram keeps asking for a new pairing code
The gateway is crashing. Fix OpenRouter auth first (Steps 04–06), then re-pair.
openclaw pairing pending → "unknown command"
Use openclaw pairing list telegram instead. The pending subcommand doesn't exist.
openclaw pairing list → "Channel required"
Always pass the channel argument: openclaw pairing list telegram
"gateway connect failed: pairing required" from inside container
Device pairing chicken-and-egg. Edit /data/.openclaw/openclaw.json directly to disable device pairing, then restart. See Step 08.
EACCES: permission denied, open '/data/.openclaw/openclaw.json'
The config file was written as root and the openclaw process can't read it. Fix: docker compose exec --user root openclaw chown openclaw:openclaw /data/.openclaw/openclaw.json, then restart.
"Unknown model: openrouter/anthropic/claude-sonnet-4-5" after fixing prefix
Usually caused by the EACCES error above. The gateway can't read the config so falls back to a broken cached model. Fix the file permissions first, then restart.
12

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

01
Blog post config used bare model refs
The routing guide used refs like anthropic/claude-sonnet-4 without the openrouter/ prefix. These work with direct API keys, not with OpenRouter as the sole provider.
02
The entrypoint rewrites config on every start
Writing [INFO] Writing OpenClaw config on startup means some env-var-driven values reset. The model, however, lives in the volume-persisted openclaw.json. So openclaw config set does work, but only if the file stays readable.
03
Editing the JSON via a Node script broke file ownership
Running docker compose exec node wrote the file as root. The openclaw process couldn't read it, causing EACCES on every config operation: status checks, session loads, channel reads. Everything.
04
Unknown model persisted even after fixing the prefix
With openclaw.json unreadable, the gateway fell back to a cached model ref. The model looked wrong in logs even though the file was correct. Because it couldn't read the file to find out.

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 -f
The auth was fine. The model prefix was fine. The only thing wrong was one file owned by root. Always fix permissions immediately after any manual file edit inside a container volume.

Everything 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.