Skip to content

Commit ec73de5

Browse files
authored
docs(runtime): clarify persistence + add /data install defaults (#139)
1 parent 73979b5 commit ec73de5

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,22 @@ RUN apt-get update \
4747
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
4848
ca-certificates \
4949
tini \
50+
python3 \
51+
python3-venv \
5052
&& rm -rf /var/lib/apt/lists/*
5153

5254
# `openclaw update` expects pnpm. Provide it in the runtime image.
5355
RUN corepack enable && corepack prepare pnpm@10.23.0 --activate
5456

57+
# Persist user-installed tools by default by targeting the Railway volume.
58+
# - npm global installs -> /data/npm
59+
# - pnpm global installs -> /data/pnpm (binaries) + /data/pnpm-store (store)
60+
ENV NPM_CONFIG_PREFIX=/data/npm
61+
ENV NPM_CONFIG_CACHE=/data/npm-cache
62+
ENV PNPM_HOME=/data/pnpm
63+
ENV PNPM_STORE_DIR=/data/pnpm-store
64+
ENV PATH="/data/npm/bin:/data/pnpm:${PATH}"
65+
5566
WORKDIR /app
5667

5768
# Wrapper deps

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,39 @@ If you’re filing a bug, please include the output of:
7171
4) Copy the **Bot Token** and paste it into `/setup`
7272
5) Invite the bot to your server (OAuth2 URL Generator → scopes: `bot`, `applications.commands`; then choose permissions)
7373

74+
## Persistence (Railway volume)
75+
76+
Railway containers have an ephemeral filesystem. Only the mounted volume at `/data` persists across restarts/redeploys.
77+
78+
What persists cleanly today:
79+
- **Custom skills / code:** anything under `OPENCLAW_WORKSPACE_DIR` (default: `/data/workspace`)
80+
- **Node global tools (npm/pnpm):** this template configures defaults so global installs land under `/data`:
81+
- npm globals: `/data/npm` (binaries in `/data/npm/bin`)
82+
- pnpm globals: `/data/pnpm` (binaries) + `/data/pnpm-store` (store)
83+
- **Python packages:** create a venv under `/data` (example below). The runtime image includes Python + venv support.
84+
85+
What does *not* persist cleanly:
86+
- `apt-get install ...` (installs into `/usr/*`)
87+
- Homebrew installs (typically `/opt/homebrew` or similar)
88+
89+
### Optional bootstrap hook
90+
91+
If `/data/workspace/bootstrap.sh` exists, the wrapper will run it on startup (best-effort) before starting the gateway.
92+
Use this to initialize persistent install prefixes or create a venv.
93+
94+
Example `bootstrap.sh`:
95+
96+
```bash
97+
#!/usr/bin/env bash
98+
set -euo pipefail
99+
100+
# Example: create a persistent python venv
101+
python3 -m venv /data/venv || true
102+
103+
# Example: ensure npm/pnpm dirs exist
104+
mkdir -p /data/npm /data/npm-cache /data/pnpm /data/pnpm-store
105+
```
106+
74107
## Troubleshooting
75108

76109
### “disconnected (1008): pairing required” / dashboard health offline

src/server.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,27 @@ const server = app.listen(PORT, "0.0.0.0", async () => {
13691369
console.warn("[wrapper] WARNING: SETUP_PASSWORD is not set; /setup will error.");
13701370
}
13711371

1372+
// Optional operator hook to install/persist extra tools under /data.
1373+
// This is intentionally best-effort and should be used to set up persistent
1374+
// prefixes (npm/pnpm/python venv), not to mutate the base image.
1375+
const bootstrapPath = path.join(WORKSPACE_DIR, "bootstrap.sh");
1376+
if (fs.existsSync(bootstrapPath)) {
1377+
console.log(`[wrapper] running bootstrap: ${bootstrapPath}`);
1378+
try {
1379+
await runCmd("bash", [bootstrapPath], {
1380+
env: {
1381+
...process.env,
1382+
OPENCLAW_STATE_DIR: STATE_DIR,
1383+
OPENCLAW_WORKSPACE_DIR: WORKSPACE_DIR,
1384+
},
1385+
timeoutMs: 10 * 60 * 1000,
1386+
});
1387+
console.log("[wrapper] bootstrap complete");
1388+
} catch (err) {
1389+
console.warn(`[wrapper] bootstrap failed (continuing): ${String(err)}`);
1390+
}
1391+
}
1392+
13721393
// Auto-start the gateway if already configured so polling channels (Telegram/Discord/etc.)
13731394
// work even if nobody visits the web UI.
13741395
if (isConfigured()) {

0 commit comments

Comments
 (0)