Docs / install / docker

Docker

Docker (optional)

Is Docker right for me?

  • Yes: you want an isolated, throwaway gateway environment or to run OpenClaw on a host without local installs.
  • No: you’re running on your own machine and just want the fastest dev loop. Use the normal install flow instead.
  • Sandboxing note: agent sandboxing uses Docker too, but it does not require the full gateway to run in Docker. See Sandboxing.
  • Containerized Gateway (full OpenClaw in Docker)
  • Per-session Agent Sandbox (host gateway + Docker-isolated agent tools)

Requirements

  • Docker Desktop (or Docker Engine) + Docker Compose v2
  • Enough disk for images + logs

Containerized Gateway (Docker Compose)

text
./docker-setup.sh
  • builds the gateway image
  • runs the onboarding wizard
  • prints optional provider setup hints
  • starts the gateway via Docker Compose
  • generates a gateway token and writes it to .env
  • OPENCLAW_DOCKER_APT_PACKAGES — install extra apt packages during build
  • OPENCLAW_EXTRA_MOUNTS — add extra host bind mounts
  • OPENCLAW_HOME_VOLUME — persist /home/node in a named volume
  • Open http://127.0.0.1:18789/ in your browser.
  • Paste the token into the Control UI (Settings → token).
  • ~/.openclaw/
  • ~/.openclaw/workspace

Manual flow (compose)

text
docker build -t openclaw:local -f Dockerfile .
docker compose run --rm openclaw-cli onboard
docker compose up -d openclaw-gateway

Extra mounts (optional)

text
export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw"
./docker-setup.sh
  • Paths must be shared with Docker Desktop on macOS/Windows.
  • If you edit OPENCLAW_EXTRA_MOUNTS, rerun docker-setup.sh to regenerate the extra compose file.
  • docker-compose.extra.yml is generated. Don’t hand-edit it.

Persist the entire container home (optional)

text
export OPENCLAW_HOME_VOLUME="openclaw_home"
./docker-setup.sh
text
export OPENCLAW_HOME_VOLUME="openclaw_home"
export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw"
./docker-setup.sh
  • If you change OPENCLAW_HOME_VOLUME, rerun docker-setup.sh to regenerate the extra compose file.
  • The named volume persists until removed with docker volume rm <name>.

Install extra apt packages (optional)

text
export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential"
./docker-setup.sh
  • This accepts a space-separated list of apt package names.
  • If you change OPENCLAW_DOCKER_APT_PACKAGES, rerun docker-setup.sh to rebuild the image.
text
FROM node:22-bookworm

# Install Bun (required for build scripts)
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:${PATH}"

RUN corepack enable

WORKDIR /app

# Cache dependencies unless package metadata changes
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY scripts ./scripts

RUN pnpm install --frozen-lockfile

COPY . .
RUN pnpm build
RUN pnpm ui:install
RUN pnpm ui:build

ENV NODE_ENV=production

CMD ["node","dist/index.js"]

Channel setup (optional)

text
docker compose run --rm openclaw-cli channels login
text
docker compose run --rm openclaw-cli channels add --channel telegram --token "<token>"
text
docker compose run --rm openclaw-cli channels add --channel discord --token "<token>"

Health check

text
docker compose exec openclaw-gateway node dist/index.js health --token "$OPENCLAW_GATEWAY_TOKEN"

E2E smoke test (Docker)

text
scripts/e2e/onboard-docker.sh

QR import smoke test (Docker)

text
pnpm test:docker:qr

Notes

  • Gateway bind defaults to lan for container use.
  • The gateway container is the source of truth for sessions (~/.openclaw/agents/<agentId>/sessions/).

Agent Sandbox (host gateway + Docker tools)

What it does

  • scope: "agent" by default (one container + workspace per agent)
  • scope: "session" for per-session isolation
  • per-scope workspace folder mounted at /workspace
  • optional agent workspace access (agents.defaults.sandbox.workspaceAccess)
  • allow/deny tool policy (deny wins)
  • inbound media is copied into the active sandbox workspace (media/inbound/*) so tools can read it (with workspaceAccess: "rw", this lands in the agent workspace)

Per-agent sandbox profiles (multi-agent)

  • Full access (personal agent)
  • Read-only tools + read-only workspace (family/work agent)
  • No filesystem/shell tools (public agent)

Default behavior

  • Image: openclaw-sandbox:bookworm-slim
  • One container per agent
  • Agent workspace access: workspaceAccess: "none" (default) uses ~/.openclaw/sandboxes
    • "ro" keeps the sandbox workspace at /workspace and mounts the agent workspace read-only at /agent (disables write/edit/apply_patch)
    • "rw" mounts the agent workspace read/write at /workspace
  • "ro" keeps the sandbox workspace at /workspace and mounts the agent workspace read-only at /agent (disables write/edit/apply_patch)
  • "rw" mounts the agent workspace read/write at /workspace
  • Auto-prune: idle > 24h OR age > 7d
  • Network: none by default (explicitly opt-in if you need egress)
  • Default allow: exec, process, read, write, edit, sessions_list, sessions_history, sessions_send, sessions_spawn, session_status
  • Default deny: browser, canvas, nodes, cron, discord, gateway
  • "ro" keeps the sandbox workspace at /workspace and mounts the agent workspace read-only at /agent (disables write/edit/apply_patch)
  • "rw" mounts the agent workspace read/write at /workspace

Enable sandboxing

  • Default docker.network is "none" (no egress).
  • readOnlyRoot: true blocks package installs.
  • user must be root for apt-get (omit user or set user: "0:0"). OpenClaw auto-recreates containers when setupCommand (or docker config) changes unless the container was recently used (within ~5 minutes). Hot containers log a warning with the exact openclaw sandbox recreate ... command.
text
{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main", // off | non-main | all
        scope: "agent", // session | agent | shared (agent is default)
        workspaceAccess: "none", // none | ro | rw
        workspaceRoot: "~/.openclaw/sandboxes",
        docker: {
          image: "openclaw-sandbox:bookworm-slim",
          workdir: "/workspace",
          readOnlyRoot: true,
          tmpfs: ["/tmp", "/var/tmp", "/run"],
          network: "none",
          user: "1000:1000",
          capDrop: ["ALL"],
          env: { LANG: "C.UTF-8" },
          setupCommand: "apt-get update && apt-get install -y git curl jq",
          pidsLimit: 256,
          memory: "1g",
          memorySwap: "2g",
          cpus: 1,
          ulimits: {
            nofile: { soft: 1024, hard: 2048 },
            nproc: 256,
          },
          seccompProfile: "/path/to/seccomp.json",
          apparmorProfile: "openclaw-sandbox",
          dns: ["1.1.1.1", "8.8.8.8"],
          extraHosts: ["internal.service:10.0.0.5"],
        },
        prune: {
          idleHours: 24, // 0 disables idle pruning
          maxAgeDays: 7, // 0 disables max-age pruning
        },
      },
    },
  },
  tools: {
    sandbox: {
      tools: {
        allow: [
          "exec",
          "process",
          "read",
          "write",
          "edit",
          "sessions_list",
          "sessions_history",
          "sessions_send",
          "sessions_spawn",
          "session_status",
        ],
        deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"],
      },
    },
  },
}

Build the default sandbox image

text
scripts/sandbox-setup.sh

Sandbox common image (optional)

text
scripts/sandbox-common-setup.sh
text
{
  agents: {
    defaults: {
      sandbox: { docker: { image: "openclaw-sandbox-common:bookworm-slim" } },
    },
  },
}

Sandbox browser image

text
scripts/sandbox-browser-setup.sh
  • Headful (Xvfb) reduces bot blocking vs headless.
  • Headless can still be used by setting agents.defaults.sandbox.browser.headless=true.
  • No full desktop environment (GNOME) is needed; Xvfb provides the display.
text
{
  agents: {
    defaults: {
      sandbox: {
        browser: { enabled: true },
      },
    },
  },
}
text
{
  agents: {
    defaults: {
      sandbox: { browser: { image: "my-openclaw-browser" } },
    },
  },
}
  • a sandbox browser control URL (for the browser tool)
  • a noVNC URL (if enabled and headless=false)

Custom sandbox image

text
docker build -t my-openclaw-sbx -f Dockerfile.sandbox .
text
{
  agents: {
    defaults: {
      sandbox: { docker: { image: "my-openclaw-sbx" } },
    },
  },
}

Tool policy (allow/deny)

  • deny wins over allow.
  • If allow is empty: all tools (except deny) are available.
  • If allow is non-empty: only tools in allow are available (minus deny).

Pruning strategy

  • prune.idleHours: remove containers not used in X hours (0 = disable)
  • prune.maxAgeDays: remove containers older than X days (0 = disable)
  • Keep busy sessions but cap lifetime: idleHours: 24, maxAgeDays: 7
  • Never prune: idleHours: 0, maxAgeDays: 0

Security notes

  • Hard wall only applies to tools (exec/read/write/edit/apply_patch).
  • Host-only tools like browser/camera/canvas are blocked by default.
  • Allowing browser in sandbox breaks isolation (browser runs on host).

Troubleshooting

  • Image missing: build with scripts/sandbox-setup.sh or set agents.defaults.sandbox.docker.image.
  • Container not running: it will auto-create per session on demand.
  • Permission errors in sandbox: set docker.user to a UID:GID that matches your mounted workspace ownership (or chown the workspace folder).
  • Custom tools not found: OpenClaw runs commands with sh -lc (login shell), which sources /etc/profile and may reset PATH. Set docker.env.PATH to prepend your custom tool paths (e.g., /custom/bin:/usr/local/share/npm-global/bin), or add a script under /etc/profile.d/ in your Dockerfile.