
This post will walk you through how to set up Openclaw in Docker. I am using Dockhand, but the steps will be similar.
Free tier LLMs to get you started (optional if you are frugal)
Grab free LLM API keys from:
- Google AI Playground
- Groq (not Grok, it is different) – https://console.groq.com/docs/rate-limits
- OpenRouter – https://openrouter.ai/openrouter/free
- Cloudflare Workers AI <– I could not get this one to work with Openclaw’s config yet, so don’t worry about it for now
Take Note: You will most likely run into heavy limitations when using Groq. Openrouter seems better.
Docker Network
If you want, create a docker network (bridge mode) just to contain your Openclaw. This is useful if you are running other containers on the same host just to isolate things a bit.
Docker Compose
Here is a docker compose file taken from the Github page of Openclaw but slightly modified for me:
services:
openclaw-gateway:
image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest}
env_file:
- .env
networks:
- openclaw_network
environment:
HOME: /home/node
OPENCLAW_HOME: /home/node
TERM: xterm-256color
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
volumes:
- ./data:/home/node/.openclaw
- ./workspace:/home/node/.openclaw/workspace
- ./secrets:/home/node/.config/openclaw
# Uncomment if running Docker-in-Docker sandboxing:
# - /var/run/docker.sock:/var/run/docker.sock
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
- "${OPENCLAW_MSTEAMS_PORT:-3978}:3978"
init: true
restart: unless-stopped
command:
[
"node",
"dist/index.js",
"gateway",
"--bind",
"${OPENCLAW_GATEWAY_BIND:-lan}",
"--port",
"18789",
]
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
openclaw-cli:
image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest}
network_mode: "service:openclaw-gateway"
env_file:
- .env
# Not needed because network_mode is defined, which piggybacks this container on the gw container
#networks:
# - openclaw_network
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
environment:
HOME: /home/node
OPENCLAW_HOME: /home/node
TERM: xterm-256color
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
BROWSER: echo
volumes:
- ./data:/home/node/.openclaw
- ./workspace:/home/node/.openclaw/workspace
- ./secrets:/home/node/.config/openclaw
stdin_open: true
tty: true
init: true
entrypoint: ["node", "dist/index.js"]
depends_on:
- openclaw-gateway
networks:
openclaw_network:
external: true
Docker env File
# Image & Ports
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_BRIDGE_PORT=18790
OPENCLAW_MSTEAMS_PORT=3978
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_TZ=Africa/Johannesburg <REPLACE WITH YOURS>
# Gateway Security & Tokens
OPENCLAW_GATEWAY_TOKEN=<GENERATE A RANDOM 32 CHARACTER HEX>
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=
OPENCLAW_DISABLE_BONJOUR=1
# -----------------------------------------------------------------------------
# LLM Provider API Keys
# -----------------------------------------------------------------------------
GEMINI_API_KEY=xxxxx
GROQ_API_KEY=xxxxx
OPENROUTER_API_KEY=xxxxx
# Cloudflare Workers AI
CLOUDFLARE_ACCOUNT_ID=xxxxx
CLOUDFLARE_API_TOKEN=xxxxx
# Optional Provider Web Session Keys
CLAUDE_AI_SESSION_KEY=
CLAUDE_WEB_SESSION_KEY=
CLAUDE_WEB_COOKIE=
# OpenTelemetry Monitoring (Optional)
OTEL_EXPORTER_OTLP_ENDPOINT=
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=openclaw
OTEL_SEMCONV_STABILITY_OPT_IN=
OPENCLAW_OTEL_PRELOADED=
Start the Stack (compose up)
You will most likely get an “EACCES” error. To fix it, do this:
Be in your Openclaw directory where it creates the data, workspace, and secrets folder. Here is mind:

# 1. Stop the stack first
docker compose down
Then cd into the directory where the folders are that the openclaw container created
# 2. Grant ownership of your local mount folders to UID 1000 (container's node user)
sudo chown -R 1000:1000 ./data ./workspace ./secrets
# 3. Ensure read/write/execute permissions are set for the owner
chmod -R 755 ./data ./workspace ./secrets
# 4. Bring the stack back up
docker compose up -d
Then you need to create a minimal config file, otherwhise your Openclaw will throw this error: Missing config. Run openclaw setup or set gateway.mode=local (or pass –allow-unconfigured).
Create this in ./data/openclaw.json
{
"gateway": {
"mode": "local",
"bind": "lan",
"port": 18789
},
"agents": {
"defaults": {
"model": {
"primary": "google/gemini-flash-latest"
}
}
}
}
Then change permissions “sudo chown 1000:1000 ./data/openclaw.json“
Full Config
If the above worked, add your full config so Openclaw will use all your free API keys. Adjust this where necessary.
- TrustedProxies section: This is your openclaw docker network range. Add it in there so that Openclaw knows this is you when connecting from your web browser.
- AllowedOrigins section: This is needed if you want to access Openclaw via HTTPS later on. We need to add this for its CORS to allow our custom domain.
{
"gateway": {
"mode": "local",
"bind": "lan",
"port": 18789,
"trustedProxies": [
"172.21.0.0/16"
],
"controlUi": {
"allowedOrigins": [
"https://openclaw.internal.leighonline.net"
]
}
},
"agents": {
"defaults": {
"model": {
"primary": "google/gemini-flash-latest",
"fallbacks": [
"groq/openai/gpt-oss-120b",
"openrouter/free"
]
}
}
}
}
Fix permissions of the file just in case “sudo chown 1000:1000 ./data/openclaw.json“
Openclaw HTTPS
To access Openclaw over HTTPS I will just be using my existing NGINX Proxy manager and attach it to my Openclaw network
services:
nginxproxymanager:
container_name: nginxproxymanager
networks:
- internal_proxy
- openclaw_network
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
#- '80:80' # Public HTTP Port. Not needed because NPM has an entry for iteself mapped to port 81
- '443:443' # Public HTTPS Port
- '81'
#- '81:81' # Admin Web Port. Not needed because NPM has an entry for iteself mapped to port 81
environment:
DISABLE_IPV6: 'true'
volumes:
- /opt/stacks/stacks/Proxmox-Production/nginxproxymanager/database:/data
- /opt/stacks/stacks/Proxmox-Production/nginxproxymanager/certs:/etc/letsencrypt
#- /opt/nginxproxymanager:/data
#- /opt/nginxproxymanager:/etc/letsencrypt
networks:
internal_proxy:
external: true
openclaw_network:
external: true
Be sure to enable websocket support.

Openclaw HTTPS
If all goes well, you will see this page. Just paste the token (the 32 character hex) you had in your env file

You will need to connect to the openclaw-cli container and approve your browser. Note that approval token.

Connect to your openclaw-cli container and run this:
docker exec -it openclaw-openclaw-cli-1 node dist/index.js devices approve 8cdxxxxxxxxxxx
Then just refresh your page and then you are in!
Install Groq Provider
To get the groq models to work you might have to do this on your docker host:
docker exec -it openclaw-openclaw-cli-1 node dist/index.js plugins install @openclaw/groq-provider
Final OpenClaw Config
I removed the free models. They are nice to play around but not good for real usage.
OpenClaw.json
{
"gateway": {
"mode": "local",
"bind": "lan",
"port": 18789,
"trustedProxies": [
"172.21.0.0/16"
],
"controlUi": {
"allowedOrigins": [
"https://openclaw.internal.leighonline.net"
]
}
},
"agents": {
"defaults": {
"model": {
"primary": "openai/gpt-5.6-luna"
}
}
},
"plugins": {
"entries": {
"groq": {
"enabled": true
},
"canvas": {
"enabled": true
}
}
},
"meta": {
"lastTouchedVersion": "2026.6.34",
"lastTouchedAt": "2026-07-25T19:42:17.998Z"
}
}
Final ENV Vars
Make sure you have the latest version of OpenClaw installed. I had an issue where I wanted to use GPT5.6-Luna, but it just did not budge and kept falling back to Gemini (my fallback model)
# Image & Ports
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_BRIDGE_PORT=18790
OPENCLAW_MSTEAMS_PORT=3978
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_TZ=Africa/Johannesburg
# Gateway Security & Tokens
OPENCLAW_GATEWAY_TOKEN=xxxxx
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=
OPENCLAW_DISABLE_BONJOUR=1
# -----------------------------------------------------------------------------
# LLM Provider API Keys
# -----------------------------------------------------------------------------
OPENAI_API_KEY=sk-proj-xxxx
#GEMINI_API_KEY=xxxx
# OpenTelemetry Monitoring (Optional)
OTEL_EXPORTER_OTLP_ENDPOINT=
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=openclaw
OTEL_SEMCONV_STABILITY_OPT_IN=
OPENCLAW_OTEL_PRELOADED=