OpenClaw
Stack
OpenClaw, an autonomous AI agent you run yourself: web dashboard, scheduled tasks, plugins, persistent workspace. This template uses the coollabsio all-in-one image (nginx + gateway, env-driven).
Image details
Source details
Configuration
TypeComposelinuxcoollabsio/openclaw:2026.7.15000:5000/data : openclawdataPORT=5000ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}AUTH_USERNAME=${AUTH_USERNAME:-admin}AUTH_PASSWORD=${AUTH_PASSWORD}OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}OPENCLAW_ALLOWED_ORIGINS=${OPENCLAW_ALLOWED_ORIGINS:-}unless-stoppedTemplate by deployable-sh·Source
Report issueStandalone Install
Select an install method, to see config/commands for deploying OpenClaw
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select OpenClaw, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
Original stackfile
The compose file this template deploys, straight from its repo:
name: openclaw
services:
openclaw:
image: coollabsio/openclaw:2026.7.1
restart: unless-stopped
environment:
PORT: "5000"
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
AUTH_USERNAME: ${AUTH_USERNAME:-admin}
AUTH_PASSWORD: ${AUTH_PASSWORD}
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
OPENCLAW_ALLOWED_ORIGINS: ${OPENCLAW_ALLOWED_ORIGINS:-}
ports:
- "5000:5000"
volumes:
- openclawdata:/data
volumes:
openclawdata:
Or deploy it directly from the source:
git clone https://github.com/deployable-sh/stacks
cd stacks
docker compose -f openclaw/compose.yaml up -dMore install options in our documentation, or see coollabsio/openclaw for app-specific guidance.
Openclaw Automated Build
Quick Start
Minimal (docker run)
docker run -d \
--name openclaw \
-p 8080:8080 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e AUTH_PASSWORD=changeme \
-e OPENCLAW_GATEWAY_TOKEN=my-secret-token \
-e OPENCLAW_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 \
-v openclaw-data:/data \
coollabsio/openclaw:latestANTHROPIC_API_KEY— any supported provider key works (OpenAI, Gemini, etc.)AUTH_PASSWORD— protects the web UI with HTTP basic auth (user defaults toadmin, override withAUTH_USERNAME)OPENCLAW_GATEWAY_TOKEN— internal API token; auto-generated if omitted, but set it explicitly for stable API accessOPENCLAW_ALLOWED_ORIGINS— (Required) Set tohttp://localhost:5173or your frontend origin to enable CORS for the Control UI/data— persists state, config, and workspace across restarts
Full Setup (docker-compose)
Includes persistent storage, browser sidecar (CDP + VNC), and webhook hooks. Seedocker-compose.yml.docker compose up -dAfter starting:
- Openclaw UI —
http://localhost:8080(login: yourAUTH_USERNAME/AUTH_PASSWORD) - Browser desktop —
http://localhost:8080/browser/(login: yourAUTH_USERNAME/ browserPASSWORD) — use this to log into sites that need auth (OAuth, 2FA, captchas). Openclaw reuses the session via CDP.
Architecture
┌─────────────────────────────────────────────┐
│ Docker container (coollabsio/openclaw) │
│ │
│ Baked in: Linuxbrew, Go, uv, build-essential│
│ Persistent volume: /data │
│ ├── .openclaw/ (state & config) │
│ └── workspace/ (user projects) │
│ │
│ ┌──────────┐ :8080 ┌────────────────┐ │
│ │ nginx │ ──────→ │ openclaw │ │
│ │ (basic │ proxy │ gateway │ │
│ │ auth) │ :18789 │ :18789 │ │
│ └──────────┘ └────────────────┘ │
│ │
│ entrypoint.sh │
│ 1. run custom init script (optional) │
│ 2. configure.js (env vars → json) │
│ 3. nginx (background) │
│ 4. exec openclaw gateway │
└─────────────────────────────────────────────┘Two-layer Docker build:
- Base image (
Dockerfile.base) — builds openclaw from source. Taggedcoollabsio/openclaw-base:<version>. - Final image (
Dockerfile) — FROM base, adds nginx + env-to-config scripts. Taggedcoollabsio/openclaw:<version>.
Files
.github/workflows/auto-update.yml — cron every 6h, check openclaw releases, build+push
.github/workflows/build.yml — CI on push/PR (build only, no push)
Dockerfile.base — multi-stage: build openclaw from source → slim runtime
Dockerfile — FROM base, add nginx + config scripts + entrypoint
scripts/configure.js — reads env vars, writes/patches openclaw.json
scripts/entrypoint.sh — container entrypoint: configure → nginx → gateway
scripts/smoke.js — smoke test (openclaw --version)
nginx/default.conf — reverse proxy :8080 → :18789, optional basic auth
.dockerignore — standard ignores
.env.example — env var referenceauto-update.yml workflow
Jobs:
1. check-release — fetch latest openclaw/openclaw release, skip if image exists
2. build-base — matrix amd64/arm64, build Dockerfile.base, push per-arch
3. merge-base-manifest — merge into coollabsio/openclaw-base:<ver> + :latest
4. build-final — matrix amd64/arm64, build Dockerfile, push per-arch
5. merge-final-manifest — merge into coollabsio/openclaw:<ver> + :latestTriggers:
schedule: '0 */6 * * *' + workflow_dispatch (version, forcerebuild, skiplatesttag).Secrets needed (repo settings)
DOCKERHUB_USERNAME— Docker Hub usernameDOCKERHUB_TOKEN— Docker Hub access tokenGITHUB_TOKEN— auto-provided by GitHub Actions
Environment variables
AI Providers (at least one required)
| Variable | Description |
|---|---|
ANTHROPICAPIKEY | Anthropic API key. Configures Claude models (Opus 4.5, Sonnet 4.5, Haiku 4.5). Set as primary when present. |
OPENAIAPIKEY | OpenAI API key. Configures GPT models (5.2, 5, 4.5-preview). Primary if no Anthropic key. |
OPENROUTERAPIKEY | OpenRouter API key. Primary if no Anthropic/OpenAI key. |
GEMINIAPIKEY | Google Gemini API key. Primary if no other provider key set. |
XAIAPIKEY | xAI API key. Configures Grok models. |
GROQAPIKEY | Groq API key. Configures Llama models on Groq hardware. |
MISTRALAPIKEY | Mistral API key. Configures Mistral Large and other models. |
CEREBRASAPIKEY | Cerebras API key. Configures Llama models on Cerebras hardware. |
VENICEAPIKEY | Venice AI API key (OpenAI-compatible). Configures Llama 3.3 70B. |
MOONSHOTAPIKEY | Moonshot API key (OpenAI-compatible). Configures Kimi K2.5. |
KIMIAPIKEY | Kimi Coding API key (Anthropic-compatible). Configures K2P5. |
MINIMAXAPIKEY | MiniMax API key (Anthropic-compatible). Configures MiniMax M2.1. |
ZAIAPIKEY | ZAI API key. Configures GLM models. |
AIGATEWAYAPIKEY | Vercel AI Gateway API key. |
OPENCODEAPIKEY | OpenCode API key. Also accepted as OPENCODEZENAPIKEY. |
SYNTHETICAPIKEY | Synthetic API key (Anthropic-compatible). |
COPILOTGITHUBTOKEN | GitHub Copilot token. Configures Claude models via GitHub. |
XIAOMIAPIKEY | Xiaomi MiMo API key (Anthropic-compatible). Configures MiMo v2 Flash. |
Multiple providers can be set simultaneously. Priority for primary model: Anthropic > OpenAI > OpenRouter > Gemini > OpenCode > GitHub Copilot > xAI > Groq > Mistral > Cerebras > Venice > Moonshot > Kimi > MiniMax > Synthetic > ZAI > AI Gateway > Xiaomi > Bedrock > Ollama.
If a provider env var is removed, that provider section is cleaned from
openclaw.json on next start.Deepgram (audio transcription, optional)
| Variable | Description |
|---|---|
DEEPGRAMAPIKEY | Deepgram API key. Enables audio transcription via Nova 3 model. |
Amazon Bedrock (uses AWS credential chain)
| Variable | Default | Description |
|---|---|---|
AWSACCESSKEYID | AWS access key. Both AWSACCESSKEYID and AWSSECRETACCESSKEY required. | |
AWSSECRETACCESSKEY | AWS secret key. | |
AWSREGION | us-east-1 | AWS region for Bedrock runtime endpoint. |
AWSSESSIONTOKEN | Optional session token for temporary credentials. | |
BEDROCKPROVIDERFILTER | "anthropic" | Filter Bedrock model discovery by provider (JSON array or comma-separated string). |
Ollama (local models, no API key needed)
| Variable | Description |
|---|---|
OLLAMABASEURL | Ollama server URL (e.g. http://host.docker.internal:11434). Enables Ollama provider when set. |
Model selection
| Variable | Description |
|---|---|
OPENCLAWPRIMARYMODEL | Override auto-selected primary model. Format: provider/model-id (e.g. anthropic/claude-sonnet-4-5-20250929). |
HTTP Basic Auth (recommended)
| Variable | Default | Description |
|---|---|---|
AUTHPASSWORD | (none) | If set, nginx enforces HTTP basic auth on all routes except /healthz and the hooks path (when hooks are enabled). If unset, no auth — gateway is open. |
AUTHUSERNAME | admin | Username for basic auth. |
Gateway
| Variable | Default | Description |
|---|---|---|
OPENCLAWGATEWAYTOKEN | (auto-generated) | Bearer token for gateway auth. Auto-generated and persisted to <STATEDIR>/gateway.token if not set. |
OPENCLAWGATEWAYPORT | 18789 | Internal port the gateway binds to. |
OPENCLAWGATEWAYBIND | loopback | Gateway bind mode. loopback = 127.0.0.1 only (nginx proxies LAN traffic). lan = 0.0.0.0 (direct access, bypasses nginx auth). Also: tailnet, auto, custom. |
OPENCLAWSTATEDIR | /data/.openclaw | Persistent state directory. Mount a volume here. |
OPENCLAWWORKSPACEDIR | /data/workspace | Workspace directory for openclaw projects. |
OPENCLAWCONFIGPATH | <STATEDIR>/openclaw.json | Override path to the config file. |
OPENCLAWCUSTOMCONFIG | /app/config/openclaw.json | Path to a user-provided custom JSON config. Env vars override on top. |
CORS / Allowed Origins (optional)
Configure Cross-Origin Resource Sharing for the Control UI. Required when accessing the gateway from a different origin (e.g., a custom web app or local dev server).| Variable | Description |
|---|---|
OPENCLAWALLOWEDORIGINS | Comma-separated list or JSON array of allowed origins. Example: http://localhost:5173,https://app.example.com or "http://localhost:5173". |
# Allow specific origins
OPENCLAW_ALLOWED_ORIGINS=http://localhost:5173,https://app.example.com
# Or as JSON array
OPENCLAW_ALLOWED_ORIGINS='["http://localhost:5173","https://app.example.com"]'Hooks (webhook automation, optional)
| Variable | Default | Description |
|---|---|---|
HOOKSENABLED | Set to true to enable the webhook hooks endpoint. | |
HOOKSTOKEN | Shared secret for hook request auth. Required by openclaw when hooks are enabled. | |
HOOKSPATH | /hooks | Path prefix for hook endpoints (/hooks/wake, /hooks/agent, etc.). |
When hooks are enabled and
AUTH_PASSWORD is set, the hooks path automatically bypasses HTTP basic auth. Openclaw validates requests using the hook token instead. Docs: https://docs.openclaw.ai/automation/webhookBrowser tool (remote CDP sidecar, optional)
| Variable | Default | Description |
|---|---|---|
BROWSERCDPURL | Remote CDP URL pointing to a browser sidecar (e.g. http://browser:9222). Required to activate browser tool. | |
BROWSEREVALUATEENABLED | false | Allow JavaScript evaluation in page context via browser actions. |
BROWSERSNAPSHOTMODE | Default snapshot mode (e.g. efficient). | |
BROWSERREMOTETIMEOUTMS | 1500 | HTTP timeout in ms for remote CDP connection. |
BROWSERREMOTEHANDSHAKETIMEOUTMS | 3000 | WebSocket handshake timeout in ms for remote CDP. |
BROWSERDEFAULTPROFILE | Override the default browser profile name. |
Requires a separate browser container connected via Docker networking. Recommended:
kasmweb/chrome (full Chrome desktop via noVNC on :6901, CDP on :9222). Docs: https://docs.openclaw.ai/tools/browserBrowser login (VNC sidecar)
For sites requiring authentication, usekasmweb/chrome so you can log in manually via a web-based desktop. Openclaw reuses the authenticated session via CDP.- Open
https://<host>:6901— full Chrome desktop via noVNC - Navigate to the target site, log in manually (handles captchas, 2FA, OAuth)
- Sessions persist in a mounted volume across restarts
- Set
BROWSER_CDP_URL=http://browser:9222— openclaw connects via CDP
Mount a persistent volume at the sidecar's profile directory (
/home/kasm-user) so cookies and sessions survive container restarts. The sidecar may need CHROME_ARGS=--remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 to expose CDP. Docs: https://docs.openclaw.ai/tools/browser-loginChannels (optional)
Note: Use these dedicated env vars for standard channel configuration. For settings not covered here, use Dot-Notation Config as a fallback.
| Variable | Default | Description |
|---|---|---|
TELEGRAMBOTTOKEN | Telegram bot token from BotFather. | |
TELEGRAMDMPOLICY | pairing | DM access policy: pairing, allowlist, open, or disabled. |
TELEGRAMALLOWFROM | Comma-separated allowlist of user IDs/usernames. Required when dmPolicy=allowlist or dmPolicy=open (use ). | |
TELEGRAMGROUPPOLICY | allowlist | Group access policy: open, allowlist, or disabled. |
TELEGRAMGROUPALLOWFROM | Comma-separated group sender allowlist (user IDs/usernames). | |
TELEGRAMREPLYTOMODE | first | Reply threading: off, first, or all. |
TELEGRAMCHUNKMODE | length | Outbound split mode: length or newline (paragraph boundaries). |
TELEGRAMTEXTCHUNKLIMIT | 4000 | Outbound text chunk size (chars). |
TELEGRAMSTREAMMODE | partial | Draft streaming: off, partial, or block. |
TELEGRAMLINKPREVIEW | true | Toggle link previews for outbound messages. |
TELEGRAMMEDIAMAXMB | 5 | Inbound/outbound media cap in MB. |
TELEGRAMREACTIONNOTIFICATIONS | own | Which reactions trigger events: off, own, or all. |
TELEGRAMREACTIONLEVEL | minimal | Agent reaction capability: off, ack, minimal, or extensive. |
TELEGRAMINLINEBUTTONS | allowlist | Inline button capability: off, dm, group, all, or allowlist. |
TELEGRAMACTIONSREACTIONS | true | Gate Telegram tool reactions. |
TELEGRAMACTIONSSTICKER | false | Gate Telegram sticker send/search actions. |
TELEGRAMPROXY | Proxy URL for Bot API calls (SOCKS/HTTP). | |
TELEGRAMWEBHOOKURL | Enable webhook mode with public endpoint URL. | |
TELEGRAMWEBHOOKSECRET | Webhook secret (optional). | |
TELEGRAMWEBHOOKPATH | /telegram-webhook | Local webhook path for incoming updates. |
TELEGRAMMESSAGEPREFIX | Prefix prepended to inbound messages. | |
DISCORDBOTTOKEN | Discord bot token. Enable MESSAGE CONTENT INTENT in Discord Developer Portal. | |
DISCORDDMPOLICY | pairing | DM access policy: pairing, allowlist, open, or disabled. |
DISCORDDMALLOWFROM | Comma-separated user IDs/names for DM allowlist. | |
DISCORDGROUPPOLICY | allowlist | Guild access policy: open, allowlist, or disabled. |
DISCORDREPLYTOMODE | off | Reply threading: off, first, or all. |
DISCORDCHUNKMODE | length | Outbound split mode: length or newline. |
DISCORDTEXTCHUNKLIMIT | 2000 | Outbound text chunk size (chars). |
DISCORDMAXLINESPERMESSAGE | 17 | Soft line limit per message. |
DISCORDMEDIAMAXMB | 8 | Inbound media cap in MB. |
DISCORDHISTORYLIMIT | 20 | Recent guild messages for context. |
DISCORDDMHISTORYLIMIT | DM history limit per user. | |
DISCORDREACTIONNOTIFICATIONS | own | Which reactions trigger events: off, own, all, or allowlist. |
DISCORDALLOWBOTS | false | Process messages from other bots. |
DISCORDMESSAGEPREFIX | Prefix prepended to inbound messages. | |
DISCORDACTIONSREACTIONS | true | Gate reaction actions. |
DISCORDACTIONSSTICKERS | true | Gate sticker send. |
DISCORDACTIONSEMOJIUPLOADS | true | Gate emoji uploads. |
DISCORDACTIONSSTICKERUPLOADS | true | Gate sticker uploads. |
DISCORDACTIONSPOLLS | true | Gate poll creation. |
DISCORDACTIONSPERMISSIONS | true | Gate channel permission edits. |
DISCORDACTIONSMESSAGES | true | Gate message read/send/edit/delete. |
DISCORDACTIONSTHREADS | true | Gate thread operations. |
DISCORDACTIONSPINS | true | Gate pin/unpin operations. |
DISCORDACTIONSSEARCH | true | Gate message search. |
DISCORDACTIONSMEMBERINFO | true | Gate member lookup. |
DISCORDACTIONSROLEINFO | true | Gate role list. |
DISCORDACTIONSCHANNELINFO | true | Gate channel info. |
DISCORDACTIONSCHANNELS | true | Gate channel management. |
DISCORDACTIONSVOICESTATUS | true | Gate voice state. |
DISCORDACTIONSEVENTS | true | Gate event management. |
DISCORDACTIONSROLES | false | Gate role add/remove. |
DISCORDACTIONSMODERATION | false | Gate timeout/kick/ban. |
SLACKBOTTOKEN | Slack bot token (xoxb-...). Both bot + app token required for Slack. | |
SLACKAPPTOKEN | Slack app token (xapp-...). | |
SLACKUSERTOKEN | Slack user token (xoxp-...). Optional, for user-level API calls. | |
SLACKSIGNINGSECRET | Signing secret for HTTP mode verification. | |
SLACKMODE | socket | Connection mode: socket or http. |
SLACKWEBHOOKPATH | /slack/events | Webhook path for HTTP mode. |
SLACKDMPOLICY | pairing | DM access policy: pairing or open. |
SLACKDMALLOWFROM | Comma-separated user IDs/handles for DM allowlist. | |
SLACKGROUPPOLICY | open | Channel access policy: open, allowlist, or disabled. |
SLACKREPLYTOMODE | off | Reply threading: off, first, or all. |
SLACKREACTIONNOTIFICATIONS | own | Which reactions trigger events: off, own, or all. |
SLACKCHUNKMODE | newline | Outbound split mode. |
SLACKTEXTCHUNKLIMIT | 4000 | Outbound text chunk size (chars). |
SLACKMEDIAMAXMB | 20 | Inbound media cap in MB. |
SLACKHISTORYLIMIT | 50 | Recent channel messages for context. |
SLACKALLOWBOTS | false | Process messages from other bots. |
SLACKMESSAGEPREFIX | Prefix prepended to inbound messages. | |
SLACKACTIONSREACTIONS | true | Gate reaction actions. |
SLACKACTIONSMESSAGES | true | Gate message read/send/edit/delete. |
SLACKACTIONSPINS | true | Gate pin/unpin operations. |
SLACKACTIONSMEMBERINFO | true | Gate member lookup. |
SLACKACTIONSEMOJILIST | true | Gate emoji list retrieval. |
WHATSAPPENABLED | Set to true to enable WhatsApp channel. Uses QR/pairing code auth at runtime. | |
WHATSAPPDMPOLICY | pairing | DM access policy: pairing, allowlist, open, or disabled. |
WHATSAPPALLOWFROM | Comma-separated E.164 phone numbers for DM allowlist. | |
WHATSAPPSELFCHATMODE | false | Enable when running on your personal WhatsApp number. |
WHATSAPPGROUPPOLICY | allowlist | Group access policy: open, disabled, or allowlist. |
WHATSAPPGROUPALLOWFROM | Comma-separated E.164 phone numbers for group sender allowlist. | |
WHATSAPPMEDIAMAXMB | 50 | Inbound media save cap in MB. |
WHATSAPPHISTORYLIMIT | 50 | Recent unprocessed messages inserted for group context. |
WHATSAPPDMHISTORYLIMIT | DM history limit in user turns. | |
WHATSAPPSENDREADRECEIPTS | true | Send read receipts (blue ticks) on message receipt. |
WHATSAPPACKREACTIONEMOJI | Emoji sent on message receipt (e.g. 👀). Omit to disable. | |
WHATSAPPACKREACTIONDIRECT | true | Send ack reactions in DM chats. |
WHATSAPPACKREACTIONGROUP | mentions | Group reaction behavior: always, mentions, or never. |
WHATSAPPMESSAGEPREFIX | Inbound message prefix. | |
WHATSAPPACTIONSREACTIONS | true | Enable WhatsApp tool reactions. |
If a channel env var is removed, that channel is cleaned from config on next start. WhatsApp env vars fully overwrite any existing WhatsApp config (no merge with custom JSON).
Provider overrides (optional)
| Variable | Description |
|---|---|
AIGATEWAYBASEURL | Custom base URL for AI gateway (e.g. Cloudflare AI Gateway). Applied to the matching provider based on URL suffix. |
ANTHROPICBASEURL | Override Anthropic API base URL specifically. |
MOONSHOTBASEURL | Override Moonshot API base URL. Default: https://api.moonshot.ai/v1. |
KIMIBASEURL | Override Kimi Coding API base URL. Default: https://api.moonshot.ai/anthropic. |
Extra system packages (optional)
| Variable | Description |
|---|---|
OPENCLAWDOCKERAPTPACKAGES | Space-separated list of apt packages to install at container startup (e.g. ffmpeg build-essential). Packages are installed before openclaw starts. Reinstalled on each container restart. |
Linuxbrew (baked into image)
The base image includes common skill dependencies baked in:- Linuxbrew —
/home/linuxbrew/.linuxbrew— skills that needbrewwork out of the box - Go —
/usr/local/go— for Go-based skills and tools - uv — fast Python package manager for Python-based skills
- build-essential, git, curl — common build dependencies
Note: packages installed at runtime (e.g. via
brew install) are part of the container filesystem and do not persist across container rebuilds. To permanently add packages, customize Dockerfile.base or use OPENCLAW_DOCKER_APT_PACKAGES for apt-available equivalents.Custom init script (optional)
| Variable | Default | Description |
|---|---|---|
OPENCLAWDOCKERINITSCRIPT | (none) | Script that runs on every container start before openclaw starts. Must be executable and idempotent. |
Port
| Variable | Default | Description |
|---|---|---|
PORT | 8080 | External port nginx listens on. |
Dot-Notation Config (OPENCLAW) — Fallback for Customization
Recommended: Use the dedicated env vars (e.g.,TELEGRAM_BOT_TOKEN,DISCORD_BOT_TOKEN) for standard configuration. Use dot-notation (OPENCLAW__*) only when you need to customize settings not covered by the dedicated vars or for quick one-off changes.
| Variable | Description |
|---|---|
OPENCLAW<path><to><key> | Set any config value using dot notation. Use suffix for arrays. |
# Use dedicated env vars for standard config:
TELEGRAM_BOT_TOKEN=your-token
TELEGRAM_TEXT_CHUNK_LIMIT=4000
# Use dot-notation ONLY for small customizations not covered above:
OPENCLAW__channels__telegram__customSetting=value
OPENCLAW__channels__discord__customSetting=valueAuto-typing:
true/false → boolean, integers → number, floats → number, otherwise string. Use [] suffix for arrays (comma-separated).Precedence (highest wins):OPENCLAW_CONFIG_JSON>OPENCLAW__*dot-notation > dedicated env vars (e.g.TELEGRAM_BOT_TOKEN) > custom JSON mount > persisted config. If both a dedicated env var and a dot-notation var set the same key, the dot-notation value wins.
JSON Config via Env Var (OPENCLAWCONFIGJSON)
Pass partial or full config as a JSON string. This is parsed after dot-notation vars, so it can override them. Invalid JSON will cause the container to exit with an error.| Variable | Description |
|---|---|
OPENCLAWCONFIGJSON | JSON object merged into config. Must be valid JSON. |
# Partial config override
OPENCLAW_CONFIG_JSON='{"gateway":{"port":8080},"channels":{"telegram":{"enabled":true}}}'
# In docker-compose (use quotes carefully)
environment:
OPENCLAW_CONFIG_JSON: '{"gateway":{"port":8080}}'Validation: If JSON is invalid, the container exits with error before starting. Use
docker logs to see the parse error.Coolify-specific (auto-set by Coolify)
| Variable | Description |
|---|---|
COOLIFYFQDN | Public FQDN assigned by Coolify. |
COOLIFYURL | Coolify dashboard URL. |
COOLIFYBRANCH | Git branch deployed. |
Custom JSON config (Docker mount)
For settings too complex for flat env vars (e.g.channels.*.groups, agent defaults, plugin config), mount a custom JSON file into the container:docker run -v ./my-openclaw.json:/app/config/openclaw.json ...Override the mount path with
OPENCLAW_CUSTOM_CONFIG env var if needed.3-tier merge order (configure.js):
- Custom JSON (
/app/config/openclaw.json) — base layer - Persisted state (
<STATE_DIR>/openclaw.json) — preserves runtime changes from previous runs - Env vars — applied on top, always win
Arrays are replaced, not concatenated. Provider API keys are always read from env vars, never from JSON.
Note: WhatsApp is a special case — when
WHATSAPP_ENABLED=true, env vars fully overwrite the WhatsApp config block (custom JSON whatsapp keys are discarded). For all other channels, custom JSON keys are preserved and env vars merge on top.Notes
- Openclaw uses CalVer:
v2026.1.29(roughly daily releases). Detected via GitHub Releases API. - Using native
ubuntu-24.04-armrunners for arm64 builds (same pattern as coollabsio/pocketbase). - Config is environment-driven: set env vars → restart container → config updates automatically.
Serve OpenClaw on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.
Proxying openclaw.example.com to http://openclaw:5000
Add this to your Caddyfile
openclaw.example.com {
reverse_proxy http://openclaw:5000
}Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs <container> - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:5000 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :5000 - Stop the other service, or pick a different host port. In
5000:5000only the left number is yours to change, the right one belongs to the app.
Running but the page won't load
The container is up but nothing appears in your browser.
- Use your server's real IP:
http://your-server-ip:5000. The 0.0.0.0 link Portainer shows isn't a real address. - Give it a minute after first deploy, openclaw can take a while to initialise.
- Make sure your firewall allows the port, e.g.
sudo ufw allow 5000
Image won't pull
Test the pull directly on the host: docker pull coollabsio/openclaw:2026.7.1
- "manifest unknown" means the tag no longer exists.
- "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64 - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect <container> --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Stack won't deploy
Compose stacks fail fast on small mistakes, and Portainer shows the reason just above the editor.
- YAML only accepts spaces for indentation, a single tab breaks the whole file.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue on coollabsio/openclaw
- Template not working: Open an issue on deployable-sh/stacks
- This website not working: Open an issue on lissy93/portainer-templates
A Compose stack
OpenClaw is a Compose stack, a set of containers defined in one file and brought up together by Portainer, then started and stopped as a single app.
The app image
An image is the app packed up ready to go, everything OpenClaw needs bundled into one download. This template pulls coollabsio/openclaw:2026.7.1, which Docker fetches once (about 2.0 GB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. OpenClaw's comes from Docker Hub, published by coollabsio.
Version tags
The bit after the colon in the image name is the version tag. This one pins 2026.7.1, so every redeploy gives you that exact build until you bump it yourself.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 5000:5000 means it's reachable on port 5000 of your server, where the left number is yours to change and the right one belongs to the app. It opens:
5000:5000
Volumes
A volume is where OpenClaw keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:
/datakept in theopenclawdatavolume Docker manages
Environment variables
Environment variables are the settings you hand over when you deploy, things like a password or a timezone. OpenClaw takes 6 of them, all with defaults you can leave alone or tweak:
PORT, defaults to5000ANTHROPIC_API_KEY, pulled from your own environmentAUTH_USERNAME, defaults toadminAUTH_PASSWORD, pulled from your own environmentOPENCLAW_GATEWAY_TOKEN, pulled from your own environmentOPENCLAW_ALLOWED_ORIGINS, pulled from your own environment. Set to the app's https domain on Miget (after first deploy):
Restart policy
The restart policy here is unless-stopped, so Docker restarts OpenClaw after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Networking
Nothing custom is set, so OpenClaw sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container openclaw. That's what you'll spot in the containers list and use in commands like docker logs openclaw.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set OpenClaw up. Add the template list to Portainer once, then deploying OpenClaw is a click rather than a wall of config.