Deploying OpenClaw
OpenClaw is a full-featured AI coding agent with a web UI, Telegram integration, and browser automation support. This guide walks through everything needed to deploy it on Pai correctly.
Required volumes
OpenClaw needs two persistent volumes:
volumes:
- name: openclaw-home
mountPath: /home/node/.openclaw
size: "5Gi"
- name: workspace
mountPath: /home/node/workspace
size: "10Gi"
| Volume | Purpose |
|---|---|
openclaw-home | Configuration, session state, agent memory, conversation history |
workspace | Working directory where the agent reads and writes code files |
Both volumes persist across pod restarts — your config edits and workspace files survive agent updates.
Required config files
Two files must be seeded into the openclaw-home volume on first start via configFiles. OpenClaw will not start correctly without them.
openclaw.json
The main OpenClaw configuration file. It tells OpenClaw which model to use, which ACP agents to run, and which channels are enabled.
configFiles:
- path: /home/node/.openclaw/openclaw.json
content: |
{
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-sonnet-4-6",
"fallbacks": []
}
},
"list": [
{
"id": "codex",
"runtime": {
"type": "acp",
"acp": {
"agent": "codex",
"backend": "acpx",
"mode": "persistent",
"cwd": "/home/node/workspace"
}
}
}
]
},
"acp": {
"enabled": true,
"dispatch": { "enabled": true },
"backend": "acpx",
"defaultAgent": "codex",
"maxConcurrentSessions": 4,
"runtime": {
"ttlMinutes": 120
}
},
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing"
}
},
"browser": {
"enabled": true,
"attachOnly": true,
"cdpUrl": "http://localhost:8082"
}
}
Key fields explained:
| Field | Description |
|---|---|
agents.defaults.model.primary | The LLM model used by default. Must match an entry in auth-profiles.json |
agents.list | List of ACP agent runtimes. Each entry has an id that is referenced by acp.defaultAgent |
agents.list[].runtime.acp.cwd | Working directory for the ACP agent — set to the workspace volume mount path |
acp.defaultAgent | Must match an id in agents.list |
acp.maxConcurrentSessions | Maximum simultaneous chat sessions |
acp.runtime.ttlMinutes | How long an idle agent session is kept alive before being recycled |
channels.telegram.enabled | Enable Telegram integration (requires a Telegram provider) |
channels.telegram.dmPolicy | pairing requires users to pair via a code before chatting |
browser.enabled | Enable browser automation features |
browser.attachOnly | Only attach to an existing browser (via CDP relay) rather than launching a new one |
browser.cdpUrl | The local CDP endpoint — keep as http://localhost:8082 when using pai relay |
Model name format: OpenClaw uses provider/model-name notation — e.g. anthropic/claude-sonnet-4-6 or google/gemini-flash-2-5. This must match the provider and key fields in auth-profiles.json.
auth-profiles.json
Tells OpenClaw which LLM auth profiles are available and how to authenticate with them. On Pai, credentials are managed by the gateway — set "key": "pai-managed" and the platform injects the real API key transparently.
- path: /home/node/.openclaw/agents/main/agent/auth-profiles.json
content: |
{
"google-gemini": {
"provider": "google",
"mode": "api-key",
"key": "pai-managed"
},
"anthropic-claude": {
"provider": "anthropic",
"mode": "api-key",
"key": "pai-managed"
}
}
Include only the profiles that correspond to model bindings attached to the agent. If you only have Gemini, omit the anthropic-claude entry (and vice versa).
"key": "pai-managed" is required. If you put a real API key here, it will be stored in the volume and visible inside the container — defeating Pai's credential isolation.
How configFiles seeding works
configFiles are seeded into the volume by an init container on first pod start using copy-no-clobber (cp -n). This means:
- First start: files are written from the YAML into the volume
- Subsequent restarts: existing files are not overwritten — your in-container edits survive
If you update configFiles in the YAML and want the change to take effect, delete the file from inside the container first:
pai exec my-agent -- rm /home/node/.openclaw/openclaw.json
# then restart the agent
pai delete agent my-agent
pai create -f my-agent.yaml
Inbound port
OpenClaw's web UI listens on port 18789. Set inbound.port to expose it:
inbound:
port: 18789
allowCIDRs:
- "YOUR.IP.ADDRESS/32" # restrict to your IP
The public URL is shown in pai list and pai status once the agent is running.
Accessing the web UI
After the agent starts, get the dashboard URL with the auth token:
pai exec my-agent -- openclaw dashboard --no-open
# Outputs: http://127.0.0.1:18789/#token=...
Replace 127.0.0.1:18789 with the agent's public URL from pai list.
Complete example
apiVersion: pai.io/v1
kind: AgentWorkload
metadata:
name: my-openclaw
spec:
image: ghcr.io/openclaw/openclaw:latest
runAsUser: 1000
modelBindings:
- claude-sonnet
providers:
- telegram-bot # optional
inbound:
port: 18789
allowCIDRs:
- "YOUR.IP.ADDRESS/32"
cdpRelay: # optional — for browser automation
token: "my-secret-token"
tokens:
maxPerDay: 50000
maxPerRequest: 8192
resources:
requests:
cpu: "1500m"
memory: "2Gi"
limits:
cpu: "2"
memory: "8Gi"
expose:
- urlPath: /downloads
directory: /home/node/.openclaw/workspace/downloads
volumes:
- name: openclaw-home
mountPath: /home/node/.openclaw
size: "5Gi"
- name: workspace
mountPath: /home/node/workspace
size: "10Gi"
configFiles:
- path: /home/node/.openclaw/openclaw.json
content: |
{
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-sonnet-4-6",
"fallbacks": []
}
},
"list": [
{
"id": "codex",
"runtime": {
"type": "acp",
"acp": {
"agent": "codex",
"backend": "acpx",
"mode": "persistent",
"cwd": "/home/node/workspace"
}
}
}
]
},
"acp": {
"enabled": true,
"dispatch": { "enabled": true },
"backend": "acpx",
"defaultAgent": "codex",
"maxConcurrentSessions": 4,
"runtime": {
"ttlMinutes": 120
}
},
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing"
}
},
"browser": {
"enabled": true,
"attachOnly": true,
"cdpUrl": "http://localhost:8082"
}
}
- path: /home/node/.openclaw/agents/main/agent/auth-profiles.json
content: |
{
"anthropic-claude": {
"provider": "anthropic",
"mode": "api-key",
"key": "pai-managed"
}
}
Optional features
| Feature | What to add |
|---|---|
| Telegram | Add a Telegram provider and set channels.telegram.enabled: true in openclaw.json |
| Browser automation | Add cdpRelay.token and run pai relay my-openclaw --token <token> on your machine. See the CDP guide |
| File downloads | The expose block serves /home/node/.openclaw/workspace/downloads at https://<url>/downloads |