Skip to content

Commit a3bbcf2

Browse files
committed
fix(docker): keep plugin runtime deps off bind mounts
1 parent 3ee5490 commit a3bbcf2

7 files changed

Lines changed: 54 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Docs: https://docs.openclaw.ai
1616
- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367. Thanks @hoyanhan and @wenxu007.
1717
- Agents/Anthropic: send implicit Anthropic beta headers only to direct public Anthropic endpoints, including OAuth, so custom Anthropic-compatible providers no longer mis-handle unsupported beta flags unless explicitly configured. Refs #73346. Thanks @byBrodowski.
1818
- Skills: require explicit `skills.entries.coding-agent.enabled` before exposing the bundled coding-agent skill, so installs with Codex on PATH but no OpenAI auth do not silently offer Codex delegation. Fixes #73358. Thanks @LaFleurAdvertising and @Sanjays2402.
19-
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock, including dist-runtime canonical roots, so Docker Desktop/WSL cold starts no longer hold `.openclaw-runtime-mirror.lock` while scanning slow persisted volumes. Fixes #73339. Thanks @1yihui.
19+
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock and keep Docker bundled plugin runtime deps/mirrors in a Docker-managed volume instead of the Windows/WSL config bind mount, so cold starts avoid slow host-volume mirror writes. Fixes #73339. Thanks @1yihui.
2020
- Channels/LINE: persist inbound image, video, audio, and file downloads in `~/.openclaw/media/inbound/` instead of temporary files so agents can still read LINE media after `/tmp` cleanup. Fixes #73370. Thanks @hijirii and @wenxu007.
2121
- CLI/plugins: keep bundled plugin installs out of `plugins.load.paths` while preserving install records, so install/inspect/doctor loops no longer warn about the current bundled plugin directory. Thanks @vincentkoc.
2222
- Control UI/WebChat: keep large attachment payloads out of Lit state and optimistic chat messages, using object URL previews plus send-time payload serialization so PDF/image uploads no longer trigger `RangeError: Maximum call stack size exceeded`. Fixes #73360; refs #54378 and #63432. Thanks @hejunhui-73, @Ansub, and @christianhernandez3-afk.

Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,12 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
258258
RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
259259
&& chmod 755 /app/openclaw.mjs
260260

261-
# Pre-create the default state dir so first-run Docker named volumes mounted
262-
# here inherit node ownership instead of starting as root-owned state.
261+
# Pre-create the default state and runtime-deps dirs so first-run Docker named
262+
# volumes mounted here inherit node ownership instead of root-owned state.
263263
RUN install -d -m 0700 -o node -g node /home/node/.openclaw && \
264-
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700'
264+
install -d -m 0700 -o node -g node /var/lib/openclaw/plugin-runtime-deps && \
265+
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700' && \
266+
stat -c '%U:%G %a' /var/lib/openclaw/plugin-runtime-deps | grep -qx 'node:node 700'
265267

266268
ENV NODE_ENV=production
267269

docker-compose.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ services:
2222
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
2323
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
2424
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
25+
OPENCLAW_PLUGIN_STAGE_DIR: /var/lib/openclaw/plugin-runtime-deps
2526
TZ: ${OPENCLAW_TZ:-UTC}
2627
volumes:
2728
- ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
2829
- ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
30+
- openclaw-plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
2931
## Uncomment the lines below to enable sandbox isolation
3032
## (agents.defaults.sandbox). Requires Docker CLI in the image
3133
## (build with --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1) or use
@@ -84,13 +86,18 @@ services:
8486
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
8587
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
8688
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
89+
OPENCLAW_PLUGIN_STAGE_DIR: /var/lib/openclaw/plugin-runtime-deps
8790
TZ: ${OPENCLAW_TZ:-UTC}
8891
volumes:
8992
- ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
9093
- ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
94+
- openclaw-plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
9195
stdin_open: true
9296
tty: true
9397
init: true
9498
entrypoint: ["node", "dist/index.js"]
9599
depends_on:
96100
- openclaw-gateway
101+
102+
volumes:
103+
openclaw-plugin-runtime-deps:

docs/install/docker-vm-runtime.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,19 @@ Expected output:
116116
OpenClaw runs in Docker, but Docker is not the source of truth.
117117
All long-lived state must survive restarts, rebuilds, and reboots.
118118

119-
| Component | Location | Persistence mechanism | Notes |
120-
| ------------------- | --------------------------------- | ---------------------- | ------------------------------------------------------------- |
121-
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, `.env` |
122-
| Model auth profiles | `/home/node/.openclaw/agents/` | Host volume mount | `agents/<agentId>/agent/auth-profiles.json` (OAuth, API keys) |
123-
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
124-
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
125-
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
126-
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
127-
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
128-
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
129-
| OS packages | Container filesystem | Docker image | Do not install at runtime |
130-
| Docker container | Ephemeral | Restartable | Safe to destroy |
119+
| Component | Location | Persistence mechanism | Notes |
120+
| ------------------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------- |
121+
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, `.env` |
122+
| Model auth profiles | `/home/node/.openclaw/agents/` | Host volume mount | `agents/<agentId>/agent/auth-profiles.json` (OAuth, API keys) |
123+
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
124+
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
125+
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
126+
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
127+
| Plugin runtime deps | `/var/lib/openclaw/plugin-runtime-deps/` | Docker named volume | Generated bundled plugin deps and runtime mirrors |
128+
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
129+
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
130+
| OS packages | Container filesystem | Docker image | Do not install at runtime |
131+
| Docker container | Ephemeral | Restartable | Safe to destroy |
131132

132133
## Updates
133134

docs/install/docker.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ The setup script accepts these optional environment variables:
129129
| `OPENCLAW_EXTENSIONS` | Pre-install plugin deps at build time (space-separated names) |
130130
| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) |
131131
| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume |
132+
| `OPENCLAW_PLUGIN_STAGE_DIR` | Container path for generated bundled plugin deps and mirrors |
132133
| `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) |
133134
| `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path |
134135
| `OPENCLAW_DISABLE_BONJOUR` | Disable Bonjour/mDNS advertising (defaults to `1` for Docker) |
@@ -267,11 +268,24 @@ That mounted config directory is where OpenClaw keeps:
267268
- `agents/<agentId>/agent/auth-profiles.json` for stored provider OAuth/API-key auth
268269
- `.env` for env-backed runtime secrets such as `OPENCLAW_GATEWAY_TOKEN`
269270

271+
Bundled plugin runtime dependencies and mirrored runtime files are generated
272+
state, not user config. Compose stores them in the named Docker volume
273+
`openclaw-plugin-runtime-deps` mounted at
274+
`/var/lib/openclaw/plugin-runtime-deps`. Keeping that high-churn tree out of the
275+
host config bind mount avoids slow Docker Desktop/WSL file operations and stale
276+
Windows handles during cold Gateway startup.
277+
278+
The default Compose file sets `OPENCLAW_PLUGIN_STAGE_DIR` to that path for both
279+
`openclaw-gateway` and `openclaw-cli`, so `openclaw doctor --fix`, channel
280+
login/setup commands, and Gateway startup all use the same generated runtime
281+
volume.
282+
270283
For full persistence details on VM deployments, see
271284
[Docker VM Runtime - What persists where](/install/docker-vm-runtime#what-persists-where).
272285

273286
**Disk growth hotspots:** watch `media/`, session JSONL files, `cron/runs/*.jsonl`,
274-
and rolling file logs under `/tmp/openclaw/`.
287+
the `openclaw-plugin-runtime-deps` Docker volume, and rolling file logs under
288+
`/tmp/openclaw/`.
275289

276290
### Shell helpers (optional)
277291

scripts/clawdock/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,14 @@ The `Dockerfile` supports two optional build args:
192192
volumes:
193193
- ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
194194
- ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
195+
- openclaw-plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
195196
```
196197
197198
This means:
198199
199200
- `~/.openclaw/.env` is available inside the container at `/home/node/.openclaw/.env` — OpenClaw loads it automatically as the global env fallback
200201
- `~/.openclaw/openclaw.json` is available at `/home/node/.openclaw/openclaw.json` — the gateway watches it and hot-reloads most changes
202+
- Generated bundled plugin runtime deps and mirrors live in the `openclaw-plugin-runtime-deps` Docker volume at `/var/lib/openclaw/plugin-runtime-deps`, not in the host config bind mount
201203
- No need to add API keys to `docker-compose.yml` or configure anything inside the container
202204
- Keys survive `clawdock-update`, `clawdock-rebuild`, and `clawdock-clean` because they live on the host
203205

src/docker-setup.e2e.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,4 +580,15 @@ describe("scripts/docker/setup.sh", () => {
580580
const compose = await readFile(join(repoRoot, "docker-compose.yml"), "utf8");
581581
expect(compose.match(/TZ: \$\{OPENCLAW_TZ:-UTC\}/g)).toHaveLength(2);
582582
});
583+
584+
it("keeps bundled plugin runtime deps on a Docker-managed volume", async () => {
585+
const compose = await readFile(join(repoRoot, "docker-compose.yml"), "utf8");
586+
expect(
587+
compose.match(/OPENCLAW_PLUGIN_STAGE_DIR: \/var\/lib\/openclaw\/plugin-runtime-deps/g),
588+
).toHaveLength(2);
589+
expect(
590+
compose.match(/- openclaw-plugin-runtime-deps:\/var\/lib\/openclaw\/plugin-runtime-deps/g),
591+
).toHaveLength(2);
592+
expect(compose).toContain("\nvolumes:\n openclaw-plugin-runtime-deps:\n");
593+
});
583594
});

0 commit comments

Comments
 (0)