Files
PaperClipAI/docs/guides/openclaw-docker-setup.md
Nicky LeachandPaperclip 38d8f37172 fix(build): enforce Node 24 across Paperclip (#11792)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip runs across the CLI, server, adapters, plugins, CI, and
container images.
> - These surfaces declared different Node.js versions from 20 through
24.
> - A newer `@types/node` major can expose APIs that the supported
runtime does not provide.
> - Node.js 20 is no longer a suitable project baseline, and Node.js 24
is the current LTS line.
> - This pull request sets Node.js 24.11.0 as one repository-wide
baseline, adds a drift check, and gives users actionable startup
guidance when their runtime is too old.
> - The benefit is one clear runtime contract for development, release,
installation, and published packages.

## Linked Issues or Issue Description

Refs #2734

Refs #11727

Refs #739

## What Changed

- Require Node.js 24.11.0 or newer in all 42 package manifests and
runtime checks.
- Use Node.js 24 in GitHub Actions, Docker images, smoke images, sandbox
setup, portable installs, and esbuild targets.
- Align every direct `@types/node` declaration on `^24.0.0`.
- Prevent Dependabot from opening major `@types/node` upgrades without a
matching runtime decision.
- Add `.nvmrc` and a CI policy check for Node version drift.
- Update ACP version gates, tests, and user documentation for the new
minimum.
- Print a non-blocking warning on CLI and server startup when Node is
unsupported, with remediation through a version manager or the
documented downloaded `install.sh` workflow.
- Deduplicate that warning when `paperclipai run` boots the CLI and
server in the same process.

## Verification

- `node scripts/check-node-version-policy.mjs`
- `node --check scripts/check-node-version-policy.mjs`
- `node --check cli/esbuild.config.mjs`
- `node --check scripts/generate-npm-package-json.mjs`
- `bash -n scripts/install.sh scripts/test-install-sh-docker.sh
scripts/e2e-install-lifecycle.sh`
- Parsed all 42 package manifests and confirmed `engines.node` is
`>=24.11.0`.
- `git diff --check`
- `vitest run
packages/adapter-utils/src/sandbox-install-command.test.ts` passed with
3 tests.
- `vitest run cli/src/node-version.test.ts` passed with 4 tests.
- Directly exercised the shared warning helper for unsupported-version
messaging and same-process deduplication.
- The focused exe.dev suite could not resolve the locally unbuilt plugin
SDK from this isolated worktree. A full offline workspace install was
also blocked because the package-manager signature verifier requires
registry access. The full suite was not run locally; draft CI performs a
clean install and evaluates the wider impact.

## Risks

- This is a breaking runtime change for users, plugins, and deployments
that still use Node.js 20 or 22.
- Published workspace packages will now produce an engine warning or
failure in strict package managers on older Node.js releases.
- Node.js 24 can reveal dependency, native module, Playwright, or agent
CLI compatibility issues in CI.
- The bootstrap installer now installs Node.js 24 when the current
runtime is older than 24.11.0.
- The portable sandbox fallback is pinned to Node.js 24.11.0 and depends
on that upstream tarball remaining available.
- Unsupported runtimes continue booting after a warning, so a later
incompatibility can still fail at its point of use.
- The CLI and server share the warning policy through the published
`@paperclipai/shared` package; packaging checks must keep that subpath
export available.
- This PR does not commit `pnpm-lock.yaml` because repository policy
assigns lockfile generation to CI.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex based on GPT-5. The exact deployment ID and context
window are not exposed in this session. Reasoning, repository tools,
shell execution, and GitHub tools were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-21 10:17:52 -07:00

11 KiB

Running OpenClaw in Docker (Local Development)

How to get OpenClaw running in a Docker container for local development and testing the Paperclip OpenClaw adapter integration.

Paperclip includes an end-to-end join smoke harness:

pnpm smoke:openclaw-join

The harness automates:

  • invite creation (allowedJoinTypes=agent)
  • OpenClaw agent join request (adapterType=openclaw)
  • board approval
  • one-time API key claim (including invalid/replay claim checks)
  • wakeup callback delivery to a dockerized OpenClaw-style webhook receiver

By default, this uses a preconfigured Docker receiver image (docker/openclaw-smoke) so the run is deterministic and requires no manual OpenClaw config edits.

Permissions note:

  • The harness performs board-governed actions (invite creation, join approval, wakeup of the new agent).
  • In authenticated mode, provide board/operator auth or the run exits early with an explicit permissions error.

One-Command OpenClaw Gateway UI (Manual Docker Flow)

To spin up OpenClaw in Docker and print a host-browser dashboard URL in one command:

pnpm smoke:openclaw-docker-ui

Default behavior is zero-flag: you can run the command as-is with no pairing-related env vars.

What this command does:

  • clones/updates openclaw/openclaw in /tmp/openclaw-docker
  • builds openclaw:local (unless OPENCLAW_BUILD=0)
  • writes isolated smoke config under ~/.openclaw-paperclip-smoke/openclaw.json and Docker .env
  • pins agent model defaults to OpenAI (openai/gpt-5.2 with OpenAI fallback)
  • starts openclaw-gateway via Compose (with required /tmp tmpfs override)
  • probes and prints a Paperclip host URL that is reachable from inside OpenClaw Docker
  • waits for health and prints:
    • http://127.0.0.1:18789/#token=...
  • disables Control UI device pairing by default for local smoke ergonomics

Environment knobs:

  • OPENAI_API_KEY (required; loaded from env or ~/.secrets)
  • OPENCLAW_DOCKER_DIR (default /tmp/openclaw-docker)
  • OPENCLAW_GATEWAY_PORT (default 18789)
  • OPENCLAW_GATEWAY_TOKEN (default random)
  • OPENCLAW_BUILD=0 to skip rebuild
  • OPENCLAW_OPEN_BROWSER=1 to auto-open the URL on macOS
  • OPENCLAW_DISABLE_DEVICE_AUTH=1 (default) disables Control UI device pairing for local smoke
  • OPENCLAW_DISABLE_DEVICE_AUTH=0 keeps pairing enabled (then approve browser with devices CLI commands)
  • OPENCLAW_MODEL_PRIMARY (default openai/gpt-5.2)
  • OPENCLAW_MODEL_FALLBACK (default openai/gpt-5.2-chat-latest)
  • OPENCLAW_CONFIG_DIR (default ~/.openclaw-paperclip-smoke)
  • OPENCLAW_RESET_STATE=1 (default) resets smoke agent state on each run to avoid stale auth/session drift
  • PAPERCLIP_HOST_PORT (default 3100)
  • PAPERCLIP_HOST_FROM_CONTAINER (default host.docker.internal)

Authenticated mode

If your Paperclip deployment is authenticated, provide auth context:

PAPERCLIP_AUTH_HEADER="Bearer <token>" pnpm smoke:openclaw-join
# or
PAPERCLIP_COOKIE="your_session_cookie=..." pnpm smoke:openclaw-join

Network topology tips

  • Local same-host smoke: default callback uses http://127.0.0.1:<port>/webhook.
  • Inside OpenClaw Docker, 127.0.0.1 points to the container itself, not your host Paperclip server.
  • For invite/onboarding URLs consumed by OpenClaw in Docker, use the script-printed Paperclip URL (typically http://host.docker.internal:3100).
  • If Paperclip rejects the container-visible host with a hostname error, allow it from host:
npx paperclipai allowed-hostname host.docker.internal

Then restart Paperclip and rerun the smoke script.

  • Docker/remote OpenClaw: prefer a reachable hostname (Docker host alias, Tailscale hostname, or public domain).
  • Authenticated/private mode: ensure hostnames are in the allowed list when required:
npx paperclipai allowed-hostname <host>

Prerequisites

  • Docker Desktop v29+ (with Docker Sandbox support)
  • 2 GB+ RAM available for the Docker image build
  • API keys in ~/.secrets (at minimum OPENAI_API_KEY)

Docker Sandbox provides better isolation (microVM-based) and simpler setup than Docker Compose. Requires Docker Desktop v29+ / Docker Sandbox v0.12+.

# 1. Clone the OpenClaw repo and build the image
git clone https://github.com/openclaw/openclaw.git /tmp/openclaw-docker
cd /tmp/openclaw-docker
docker build -t openclaw:local -f Dockerfile .

# 2. Create the sandbox using the built image
docker sandbox create --name openclaw -t openclaw:local shell ~/.openclaw/workspace

# 3. Allow network access to OpenAI API
docker sandbox network proxy openclaw \
  --allow-host api.openai.com \
  --allow-host localhost

# 4. Write the config inside the sandbox
docker sandbox exec openclaw sh -c '
mkdir -p /home/node/.openclaw/workspace /home/node/.openclaw/identity /home/node/.openclaw/credentials
cat > /home/node/.openclaw/openclaw.json << INNEREOF
{
  "gateway": {
    "mode": "local",
    "port": 18789,
    "bind": "loopback",
    "auth": {
      "mode": "token",
      "token": "sandbox-dev-token-12345"
    },
    "controlUi": { "enabled": true }
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "openai/gpt-5.2",
        "fallbacks": ["openai/gpt-5.2-chat-latest"]
      },
      "workspace": "/home/node/.openclaw/workspace"
    }
  }
}
INNEREOF
chmod 600 /home/node/.openclaw/openclaw.json
'

# 5. Start the gateway (pass your API key from ~/.secrets)
source ~/.secrets
docker sandbox exec -d \
  -e OPENAI_API_KEY="$OPENAI_API_KEY" \
  -w /app openclaw \
  node dist/index.js gateway --bind loopback --port 18789

# 6. Wait ~15 seconds, then verify
sleep 15
docker sandbox exec openclaw curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/
# Should print: 200

# 7. Check status
docker sandbox exec -e OPENAI_API_KEY="$OPENAI_API_KEY" -w /app openclaw \
  node dist/index.js status

Sandbox Management

# List sandboxes
docker sandbox ls

# Shell into the sandbox
docker sandbox exec -it openclaw bash

# Stop the sandbox (preserves state)
docker sandbox stop openclaw

# Remove the sandbox
docker sandbox rm openclaw

# Check sandbox version
docker sandbox version

Option B: Docker Compose (Fallback)

Use this if Docker Sandbox is not available (Docker Desktop < v29).

# 1. Clone the OpenClaw repo
git clone https://github.com/openclaw/openclaw.git /tmp/openclaw-docker
cd /tmp/openclaw-docker

# 2. Build the Docker image (~5-10 min on first run)
docker build -t openclaw:local -f Dockerfile .

# 3. Create config directories
mkdir -p ~/.openclaw/workspace ~/.openclaw/identity ~/.openclaw/credentials
chmod 700 ~/.openclaw ~/.openclaw/credentials

# 4. Generate a gateway token
export OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)
echo "Your gateway token: $OPENCLAW_GATEWAY_TOKEN"

# 5. Create the config file
cat > ~/.openclaw/openclaw.json << EOF
{
  "gateway": {
    "mode": "local",
    "port": 18789,
    "bind": "lan",
    "auth": {
      "mode": "token",
      "token": "$OPENCLAW_GATEWAY_TOKEN"
    },
    "controlUi": {
      "enabled": true,
      "allowedOrigins": ["http://127.0.0.1:18789"]
    }
  },
  "env": {
    "OPENAI_API_KEY": "\${OPENAI_API_KEY}"
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "openai/gpt-5.2",
        "fallbacks": ["openai/gpt-5.2-chat-latest"]
      },
      "workspace": "/home/node/.openclaw/workspace"
    }
  }
}
EOF
chmod 600 ~/.openclaw/openclaw.json

# 6. Create the .env file (load API keys from ~/.secrets)
source ~/.secrets
cat > .env << EOF
OPENCLAW_CONFIG_DIR=$HOME/.openclaw
OPENCLAW_WORKSPACE_DIR=$HOME/.openclaw/workspace
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_BRIDGE_PORT=18790
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_GATEWAY_TOKEN=$OPENCLAW_GATEWAY_TOKEN
OPENCLAW_IMAGE=openclaw:local
OPENAI_API_KEY=$OPENAI_API_KEY
OPENCLAW_EXTRA_MOUNTS=
OPENCLAW_HOME_VOLUME=
OPENCLAW_DOCKER_APT_PACKAGES=
EOF

# 7. Add tmpfs to docker-compose.yml (required — see Known Issues)
# Add to BOTH openclaw-gateway and openclaw-cli services:
#   tmpfs:
#     - /tmp:exec,size=512M

# 8. Start the gateway
docker compose up -d openclaw-gateway

# 9. Wait ~15 seconds for startup, then get the dashboard URL
sleep 15
docker compose run --rm openclaw-cli dashboard --no-open

The dashboard URL will look like: http://127.0.0.1:18789/#token=<your-token>

Docker Compose Management

cd /tmp/openclaw-docker

# Stop
docker compose down

# Start again (no rebuild needed)
docker compose up -d openclaw-gateway

# View logs
docker compose logs -f openclaw-gateway

# Check status
docker compose run --rm openclaw-cli status

# Get dashboard URL
docker compose run --rm openclaw-cli dashboard --no-open

Known Issues and Fixes

"no space left on device" when starting containers

Docker Desktop's virtual disk may be full.

docker system df                   # check usage
docker system prune -f             # remove stopped containers, unused networks
docker image prune -f              # remove dangling images

"Unable to create fallback OpenClaw temp dir: /tmp/openclaw-1000" (Compose only)

The container can't write to /tmp. Add a tmpfs mount to docker-compose.yml for both services:

services:
  openclaw-gateway:
    tmpfs:
      - /tmp:exec,size=512M
  openclaw-cli:
    tmpfs:
      - /tmp:exec,size=512M

This issue does not affect the Docker Sandbox approach.

Node version mismatch in community template images

Some community-built sandbox templates (e.g. olegselajev241/openclaw-dmr:latest) ship Node 20, but OpenClaw requires Node >=22.12.0. Use our locally built openclaw:local image as the sandbox template instead, which includes Node 24.

Gateway takes ~15 seconds to respond after start

The Node.js gateway needs time to initialize. Wait 15 seconds before hitting http://127.0.0.1:18789/.

CLAUDE_AI_SESSION_KEY warnings (Compose only)

These Docker Compose warnings are harmless and can be ignored:

level=warning msg="The \"CLAUDE_AI_SESSION_KEY\" variable is not set. Defaulting to a blank string."

Configuration

Config file: ~/.openclaw/openclaw.json (JSON5 format)

Key settings:

  • gateway.auth.token — the auth token for the web UI and API
  • agents.defaults.model.primary — the AI model (use openai/gpt-5.2 or newer)
  • env.OPENAI_API_KEY — references the OPENAI_API_KEY env var (Compose approach)

API keys are stored in ~/.secrets and passed into containers via env vars.

Reference