fix(docker): atomized volume setup and migration robustness
- src/infra/state-migrations.ts: Handle EBUSY errors on rename (Docker bind mount safe) - Dockerfile: Add global CLI symlink and fix volume permissions - docker-compose.yml: Use named volumes for isolation and explicit entrypoints
This commit is contained in:
parent
4583f88626
commit
c7bfcadf02
@ -32,6 +32,13 @@ RUN pnpm ui:build
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Create global CLI symlink
|
||||
RUN ln -s /app/moltbot.mjs /usr/local/bin/moltbot
|
||||
|
||||
# Ensure state directories exist and are owned by node
|
||||
RUN mkdir -p /var/lib/moltbot /home/node/clawd && \
|
||||
chown -R node:node /var/lib/moltbot /home/node/clawd
|
||||
|
||||
# Security hardening: Run as non-root user
|
||||
# The node:22-bookworm image includes a 'node' user (uid 1000)
|
||||
# This reduces the attack surface by preventing container escape via root privileges
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
services:
|
||||
moltbot-gateway:
|
||||
container_name: moltbot
|
||||
image: ${CLAWDBOT_IMAGE:-moltbot:local}
|
||||
environment:
|
||||
HOME: /home/node
|
||||
MOLTBOT_STATE_DIR: /var/lib/moltbot
|
||||
TERM: xterm-256color
|
||||
CLAWDBOT_GATEWAY_TOKEN: ${CLAWDBOT_GATEWAY_TOKEN}
|
||||
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
|
||||
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
|
||||
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
|
||||
volumes:
|
||||
- ${CLAWDBOT_CONFIG_DIR}:/home/node/.clawdbot
|
||||
- ${CLAWDBOT_WORKSPACE_DIR}:/home/node/clawd
|
||||
- moltbot_data:/var/lib/moltbot
|
||||
- moltbot_workspace:/home/node/clawd
|
||||
ports:
|
||||
- "${CLAWDBOT_GATEWAY_PORT:-18789}:18789"
|
||||
- "${CLAWDBOT_BRIDGE_PORT:-18790}:18790"
|
||||
@ -19,27 +21,15 @@ services:
|
||||
command:
|
||||
[
|
||||
"node",
|
||||
"dist/index.js",
|
||||
"/app/moltbot.mjs",
|
||||
"gateway",
|
||||
"--bind",
|
||||
"${CLAWDBOT_GATEWAY_BIND:-lan}",
|
||||
"--port",
|
||||
"${CLAWDBOT_GATEWAY_PORT:-18789}"
|
||||
"${CLAWDBOT_GATEWAY_PORT:-18789}",
|
||||
"--allow-unconfigured"
|
||||
]
|
||||
|
||||
moltbot-cli:
|
||||
image: ${CLAWDBOT_IMAGE:-moltbot:local}
|
||||
environment:
|
||||
HOME: /home/node
|
||||
TERM: xterm-256color
|
||||
BROWSER: echo
|
||||
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
|
||||
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
|
||||
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
|
||||
volumes:
|
||||
- ${CLAWDBOT_CONFIG_DIR}:/home/node/.clawdbot
|
||||
- ${CLAWDBOT_WORKSPACE_DIR}:/home/node/clawd
|
||||
stdin_open: true
|
||||
tty: true
|
||||
init: true
|
||||
entrypoint: ["node", "dist/index.js"]
|
||||
volumes:
|
||||
moltbot_data:
|
||||
moltbot_workspace:
|
||||
|
||||
@ -21,11 +21,11 @@ if ! docker compose version >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${CLAWDBOT_CONFIG_DIR:-$HOME/.clawdbot}"
|
||||
mkdir -p "${CLAWDBOT_WORKSPACE_DIR:-$HOME/clawd}"
|
||||
mkdir -p "${CLAWDBOT_CONFIG_DIR:-$HOME/.moltbot}"
|
||||
mkdir -p "${CLAWDBOT_WORKSPACE_DIR:-$HOME/moltbot}"
|
||||
|
||||
export CLAWDBOT_CONFIG_DIR="${CLAWDBOT_CONFIG_DIR:-$HOME/.clawdbot}"
|
||||
export CLAWDBOT_WORKSPACE_DIR="${CLAWDBOT_WORKSPACE_DIR:-$HOME/clawd}"
|
||||
export CLAWDBOT_CONFIG_DIR="${CLAWDBOT_CONFIG_DIR:-$HOME/.moltbot}"
|
||||
export CLAWDBOT_WORKSPACE_DIR="${CLAWDBOT_WORKSPACE_DIR:-$HOME/moltbot}"
|
||||
export CLAWDBOT_GATEWAY_PORT="${CLAWDBOT_GATEWAY_PORT:-18789}"
|
||||
export CLAWDBOT_BRIDGE_PORT="${CLAWDBOT_BRIDGE_PORT:-18790}"
|
||||
export CLAWDBOT_GATEWAY_BIND="${CLAWDBOT_GATEWAY_BIND:-lan}"
|
||||
@ -62,7 +62,7 @@ YAML
|
||||
|
||||
if [[ -n "$home_volume" ]]; then
|
||||
printf ' - %s:/home/node\n' "$home_volume" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/.clawdbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/.moltbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/clawd\n' "$CLAWDBOT_WORKSPACE_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
fi
|
||||
|
||||
@ -77,7 +77,7 @@ YAML
|
||||
|
||||
if [[ -n "$home_volume" ]]; then
|
||||
printf ' - %s:/home/node\n' "$home_volume" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/.clawdbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/.moltbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
printf ' - %s:/home/node/clawd\n' "$CLAWDBOT_WORKSPACE_DIR" >>"$EXTRA_COMPOSE_FILE"
|
||||
fi
|
||||
|
||||
|
||||
@ -360,7 +360,13 @@ export async function autoMigrateLegacyStateDir(params: {
|
||||
|
||||
try {
|
||||
fs.renameSync(legacyDir, targetDir);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
if (err.code === "EBUSY") {
|
||||
warnings.push(
|
||||
`Legacy state dir (${legacyDir}) could not be renamed (EBUSY). This is common in Docker with bind mounts. Skipping automatic migration.`,
|
||||
);
|
||||
return { migrated: false, skipped: true, changes: [], warnings };
|
||||
}
|
||||
warnings.push(`Failed to move legacy state dir (${legacyDir} → ${targetDir}): ${String(err)}`);
|
||||
return { migrated: false, skipped: false, changes, warnings };
|
||||
}
|
||||
@ -430,11 +436,11 @@ export async function detectLegacyStateMigrations(params: {
|
||||
: { store: {}, ok: true };
|
||||
const legacyKeys = targetSessionParsed.ok
|
||||
? listLegacySessionKeys({
|
||||
store: targetSessionParsed.store,
|
||||
agentId: targetAgentId,
|
||||
mainKey: targetMainKey,
|
||||
scope: targetScope,
|
||||
})
|
||||
store: targetSessionParsed.store,
|
||||
agentId: targetAgentId,
|
||||
mainKey: targetMainKey,
|
||||
scope: targetScope,
|
||||
})
|
||||
: [];
|
||||
|
||||
const legacyAgentDir = path.join(stateDir, "agent");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user