Overview
OpenClaw is a self-hosted, always-on AI assistant: one Gateway that connects the model of your choice to your chat apps (Telegram, Slack, Discord, WhatsApp) and gives it tools, including a shell, files, a headless browser, scheduled jobs, memory and MCP servers, with a built-in browser Control UI. This template runs the Gateway as a single stateful replica with all of its state on a snapshotted volume. You bring the model key: Anthropic, OpenAI, or any OpenAI-compatible endpoint. The install is private by default. You reach the Control UI overcpln port-forward for setup, and turn on public access (or a custom domain) for daily use. The Control Plane MCP server is pre-registered, so the assistant can manage your Control Plane resources once someone signs in.
What Gets Created
Prerequisites
Create the dictionary secret
secret.name to this name. Channel keys can be added later; see Connecting Chat Channels.Use a single-location GVC
Optional: a custom domain
Installation
Install from the marketplace registry with the one required value, the name of the secret you created:claude-opus-5-5; add --set model.provider=... and --set model.name=... for another (see Model). Then follow the instructions for your preferred method:
UI
CLI
Terraform
Pulumi
Configuration
One subsection per values area. Each block shows the shipped defaults. Values seed OpenClaw’s config on the first boot. Afterwards, ahelm upgrade re-applies model, channels, httpApi and cplnMcp only when those values change; settings you change in the Control UI persist otherwise. A value change restarts the Gateway, so the assistant is briefly offline. An upgrade only carries the values you pass it, so always repeat the ones you installed with:
Secret
secret.name— Name of the dictionary secret from Prerequisites. The keys it must contain depend on the channels you enable. Agateway-tokenshorter than 24 characters stops the Gateway at start (see Troubleshooting).
Model
model.provider—anthropic,openai, orcustomfor any OpenAI-compatible endpoint, such as OpenRouter or a LiteLLM gateway. The key is alwaysllm-api-keyin your secret.model.name— The bare model ID from your provider. The chart adds the provider prefix itself and refuses a name containing/.model.baseUrl— Required withcustomand refused with the other providers. Usually ends in/v1.
model.provider on a running install, rebuild the memory index; see Important Notes.
Channels
Browser Tool and HTTP API
browser.enabled— Run the image variant with headless Chromium, so the agent can open and read web pages. With it off,resources.maxMemory: 2Giis enough.httpApi.enabled— Serve an OpenAI-compatible API at/v1/chat/completionsand/v1/responseson port18789, authenticated with the gateway token. See Connecting for the headers each caller needs.
Control Plane MCP
cplnMcp.enabled— Register the Control Plane MCP server ascpln. Nobody is signed in at install, so it grants nothing until someone signs in.cplnMcp.signIn—shared(default): you sign in once from a terminal and every chat acts with your Control Plane account.per-requester: each chat user signs in with their own account from a chat channel.per-requesterrequirespublicAccess.enabled: true; the chart refuses to render it otherwise.
Resources and Storage
image.tag— The exact OpenClaw release. Take a snapshot before changing it; see Backing Up.resources— CPU and memory reserved (min*) and the limits (max*). On a stateful workloadmaxCpumay be at most 4 timesminCpu; the chart refuses a wider ratio.volumeset.capacity— Initial volume size in GiB (minimum 10). Withvolumeset.autoscaling.enabledthe volume grows byscalingFactorwhen free space drops belowminFreePercentage, up tomaxCapacity.backup.*— Scheduled volume snapshots.scheduleis a 5-field cron in UTC with a single fixed minute, because the platform runs snapshots at most hourly; the chart refuses a schedule such as*/10 * * * *.
Access
publicAccess.enabled— Serve the Control UI on the public canonical HTTPS endpoint. While it is off, requests from the internet are refused and the UI is reachable overcpln port-forwardonly.publicAccess.origin— The https origin of a custom domain (no path, no trailing slash). RequirespublicAccess.enabled: true. See Custom Domain.internalAccess.type— Which workloads may call port18789from inside Control Plane:
Connecting
200:
First Run
On the port-forward, sign in tohttp://localhost:18789 with the gateway token; that browser is approved automatically. On the public endpoint or a custom domain each new browser needs a one-time approval:
Read the gateway token
gateway-token.Sign in
Approve the browser
Custom Domain
Create a Control Plane domain routed to port18789 of RELEASE_NAME-openclaw, then set publicAccess.origin to its https origin, for example https://assistant.example.com. The canonical endpoint is then refused by the origin check. After an uninstall and reinstall, delete and recreate the domain: it does not rebind to the new workload by itself.
Connecting Chat Channels
Telegram, Slack and Discord connect outbound, so they work on a private install too. For each one:Create the bot and add its token to your secret
data, and re-apply it:secret.yaml afterwards; it holds your keys in plain text.Enable the channel
channels.<name>.enabled: true and run cpln helm upgrade with your other values. The Gateway restarts (about 2 minutes) and connects. Check with channels status (see Restarting the Gateway or a Channel); it should read running, connected. A token changed later needs cpln workload force-redeployment.DM the bot and approve the pairing code
slack or discord instead of telegram as needed:channels.whatsapp.enabled: true, open the Control UI over the public endpoint → Settings → Channels → WhatsApp → Show QR, then on the phone go to Settings → Linked devices → Link a device. The image does not refresh, so click Show QR again after about 60 seconds. Do not abandon a half-done setup tab; it blocks a new one for about 5 minutes. On a private install, print the QR in a terminal instead:
Connecting Control Plane Tools (MCP)
WithcplnMcp.enabled the Control Plane MCP server is registered as cpln; nobody is signed in at install. Pick the mode by where you want to sign in. The two are not interchangeable:
127.0.0.1:8989 and fails with connection refused. Instead run these two commands in two terminals; the tunnel carries the browser’s return to the Gateway:
cpln port-forward.
Per-requester: sign in from a chat. This needs publicAccess.enabled, because the link returns to the Gateway’s public address. Connect a chat channel first, then in a DM ask the assistant to use Control Plane (for example “list my GVCs”). It replies with a sign-in link; open it, approve with your own Control Plane account, and ask again. Each chat identity signs in once, so your Slack and Discord accounts each get their own link. Links are single-use and whoever opens one connects their account, so do not request them in group chats. The Control UI chat cannot sign in in this mode. To switch an existing install, set cplnMcp.signIn and run cpln helm upgrade, then ask for a new link; links issued in the old mode do not work.
Check either mode:
oauth: authorized; per-requester reads connected principals: N.
Operations
Backing Up
Backups are scheduled volume snapshots managed by the platform; no cloud account or bucket is required. By default the volume is snapshotted daily at 03:00 UTC and each snapshot is kept for 7 days (backup.schedule, backup.retention). Snapshots contain your WhatsApp session and MCP sign-in tokens, so treat them as secrets.
List the snapshots of the release’s volume set:
image.tag. A new OpenClaw release runs one-way state migrations, so an older release cannot read the migrated state. Read your GVC’s location from spec.staticPlacement.locationLinks with cpln gvc get GVC_NAME -o yaml, then:
Restoring a Backup
A snapshot restores in place, onto the release’s own volume set; the platform then replaces the replica and the Gateway boots on the restored state. There is no path to restore into a different or reinstalled release.Find the snapshot
Restore it
0:Verify
cpln workload get-deployments RELEASE_NAME-openclaw --gvc GVC_NAME -o yaml, then sign in and confirm your sessions and settings. To roll back an image upgrade, restore the snapshot you took before it and upgrade again with the previous image.tag.Restarting the Gateway or a Channel
Restart the Gateway in place. It takes about 15 seconds after any running agent turn finishes, and keeps serving while it waits; channels and open Control UI tabs reconnect:channels status. The start call returns "started": true even if the channel fails moments later, for example with a bad token, so the status is what to read:
cpln workload force-redeployment RELEASE_NAME-openclaw --gvc GVC_NAME only as a last resort (it takes the assistant offline for about 4 minutes), or after changing a key in your secret, because a new key reaches only a new replica. Pairings, the WhatsApp link and MCP sign-ins survive it.
Scaling and Availability
The Gateway runs as exactly one replica, with noreplicas value: OpenClaw supports a single Gateway per state directory, and each bot token can hold only one connection. A restart, a value change or a platform reschedule is therefore a short full outage, after which the Gateway comes back on the same volume with its state intact. Scale vertically with resources, keeping maxCpu at most 4 times minCpu. For more isolation, such as one assistant per team, install another release with its own secret and bot tokens.
Troubleshooting
Workload never becomes ready and cpln logs returns nothing
Workload never becomes ready and cpln logs returns nothing
cpln helm install reported success, the workload has no ready replica, and cpln logs returns zero lines. cpln workload get-deployments RELEASE_NAME-openclaw --gvc GVC_NAME -o yaml shows under status.versions[].message:secret.name names a secret that does not exist, or a channel was enabled before its key was in the secret.Fix: Create the secret, or add the missing key, as in Prerequisites. The workload recovers on its own after a wait, or run cpln workload force-redeployment RELEASE_NAME-openclaw --gvc GVC_NAME to skip it.Gateway exits at start with gateway-token must be at least 24 characters
Gateway exits at start with gateway-token must be at least 24 characters
gateway-token key is too short.Fix: Replace it with a longer value (export, edit and re-apply the secret as in Connecting Chat Channels), then run cpln workload force-redeployment RELEASE_NAME-openclaw --gvc GVC_NAME. A running replica does not pick up a changed key by itself.HTTP API returns 403 proxy_attribution_required
HTTP API returns 403 proxy_attribution_required
/v1/chat/completions fails with 403 proxy_attribution_required even with the correct bearer token.Cause: In-GVC callers must send an X-Forwarded-For header with a non-loopback address. A missing header and 127.0.0.1 are refused alike.Fix: Add a header such as X-Forwarded-For: 10.0.0.1 alongside Authorization: Bearer.Control Plane sign-in link from chat fails with connection refused
Control Plane sign-in link from chat fails with connection refused
shared mode you asked the bot to sign in to Control Plane; the link it sent ends at 127.0.0.1:8989 with connection refused.Cause: Shared sign-in completes only from a terminal.Fix: Sign in with the port-forward and mcp login cpln commands in Connecting Control Plane Tools (MCP), or switch to cplnMcp.signIn: per-requester on a public install and ask for a new link.You've reached your Codex subscription usage limit
You've reached your Codex subscription usage limit
model.provider: openai, replies fail with You’ve reached your Codex subscription usage limit.Cause: Most likely your OpenAI API account is out of credit or quota. OpenClaw runs openai/* models on its Codex runtime, which words billing errors this way.Fix: Check the credit and quota of the OpenAI account behind llm-api-key. To see the provider’s real error (for example credit_balance_exhausted), pin the model to the openclaw runtime with agents.defaults.models["openai/MODEL"].agentRuntime.id.Slack channel or Discord server messages are ignored
Slack channel or Discord server messages are ignored
channel-not-allowed) or a Discord server (the Control UI shows Discord as needs attention). Or a Slack DM shows Sending messages to this app has been turned off.Cause: Only DMs are answered until a channel or server is allowlisted. The Slack DM message is an app setting.Fix: Allowlist the channel or server as described in the upstream Slack and Discord docs. For the Slack DM message, open App Home → Messages Tab, allow messages, then reload Slack.Memory search returns nothing after changing the model provider
Memory search returns nothing after changing the model provider
model.provider, the assistant cannot recall anything it saved.Cause: The memory index was built for the previous provider.Fix: Rebuild it:Custom domain stopped working after a reinstall
Custom domain stopped working after a reinstall
18789 of RELEASE_NAME-openclaw.Public endpoint still refused after enabling public access
Public endpoint still refused after enabling public access
publicAccess.enabled: true and the canonical endpoint still refuses requests.Cause: An access change restarts the Gateway and takes from about 30 seconds to 10 minutes to take effect.Fix: Wait and re-test. If it persists, confirm the upgrade carried publicAccess.enabled=true with cpln workload get RELEASE_NAME-openclaw --gvc GVC_NAME -o yaml.Important Notes
- Approve only chat contacts you would trust as admins. An approved contact can have the agent run shell commands in the container without asking, and those commands can read the gateway token and the workload’s Control Plane token; per-requester MCP sign-in does not change that. To hold each command for your approval, run
node openclaw.mjs config set tools.exec.ask alwaysthroughcpln workload execas in Connecting. - With
publicAccess.enabled, an agent with a shell is on the internet behind one token. Keep the gateway token long and secret, keep DM pairing on, and vet third-party skills. - Every new chat contact gets a pairing code and is ignored until you approve it with
pairing approve CHANNEL CODE. - Take a volume snapshot before changing
image.tag. Image upgrades run one-way state migrations, so a downgrade cannot read migrated state. - The assistant spends model tokens on its own. OpenClaw runs a heartbeat turn every 30 minutes by default; change or turn it off in the Control UI.
- After changing
model.provider, rebuild the memory index withnode openclaw.mjs memory status --index --agent main; until then memory search returns nothing. - The Control UI microphone only dictates until you choose a voice provider. For spoken conversation (Talk), run
node openclaw.mjs config set talk.realtime.provider openai. - To run sessions on another machine (a laptop, a VM), pair it as a session host using the public endpoint. With
model.provider: openaiit also needs the Codex placement steps, or it stays greyed out in the picker. - A changed key in your secret needs a forced redeployment to reach the Gateway, which takes the assistant offline for about 4 minutes.
- A custom domain must be deleted and recreated after a reinstall; it does not rebind to the new workload.
- Uninstall deletes the volume and all state, including the WhatsApp link, MCP sign-ins, sessions and workspace, with no tested way to restore it into a new install. Copy out anything you need first. Your own prerequisite secret is not deleted.