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:

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:

Openclaw folders it created when you started the compose file
# 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.

NGINX Proxy Manager custom domain with websockets enabled. Remember to update Openclaw's config with this domain.


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

Openclaw HTTPS hex token.


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

Browser 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=

necrolingus

Tech enthusiast and home labber