# syntax=docker/dockerfile:1.7# Opt-in extension dependencies at build time (space-separated directory names).# Example: docker build --build-arg OPENCLAW_EXTENSIONS="diagnostics-otel matrix" .## Multi-stage build produces a minimal runtime image without build tools,# source code, or Bun. Works with Docker, Buildx, and Podman.# The ext-deps stage extracts only the package.json files we need from the# bundled plugin workspace tree, so the main build layer is not invalidated by# unrelated plugin source changes.## Two runtime variants:# Default (bookworm): docker build .# Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim .ARG OPENCLAW_EXTENSIONS=""ARG OPENCLAW_VARIANT=defaultARG OPENCLAW_BUNDLED_PLUGIN_DIR=extensionsARG OPENCLAW_DOCKER_APT_UPGRADE=1ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="node:24-bookworm-slim@sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb"ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb"# Base images are pinned to SHA256 digests for reproducible builds.# Trade-off: digests must be updated manually when upstream tags move.# To update, run: docker buildx imagetools inspect node:24-bookworm (or podman)# and replace the digest below with the current multi-arch manifest list entry.FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-depsARG OPENCLAW_EXTENSIONSARG OPENCLAW_BUNDLED_PLUGIN_DIRCOPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}# Copy package.json for opted-in extensions so pnpm resolves their deps.RUN mkdir -p /out && \for ext in $OPENCLAW_EXTENSIONS; do \if [ -f "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" ]; then \mkdir -p "/out/$ext" && \cp "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" "/out/$ext/package.json"; \fi; \done# ── Stage 2: Build ──────────────────────────────────────────────FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS buildARG OPENCLAW_BUNDLED_PLUGIN_DIR# Install Bun (required for build scripts). Retry the whole bootstrap flow to# tolerate transient 5xx failures from bun.sh/GitHub during CI image builds.RUN set -eux; \for attempt in 1 2 3 4 5; do \if curl --retry 5 --retry-all-errors --retry-delay 2 -fsSL https://bun.sh/install | bash; then \break; \fi; \if [ "$attempt" -eq 5 ]; then \exit 1; \fi; \sleep $((attempt * 2)); \doneENV PATH="/root/.bun/bin:${PATH}"RUN corepack enableWORKDIR /appCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./COPY ui/package.json ./ui/package.jsonCOPY patches ./patchesCOPY scripts/postinstall-bundled-plugins.mjs scripts/npm-runner.mjs ./scripts/COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/# Reduce OOM risk on low-memory hosts during dependency installation.# Docker builds on small VMs may otherwise fail with "Killed" (exit 137).RUN --mount=type=cache,id=openclaw-pnpm-store,target=/root/.local/share/pnpm/store,sharing=locked \NODE_OPTIONS=--max-old-space-size=2048 pnpm install --frozen-lockfileCOPY . .# Normalize extension paths now so runtime COPY preserves safe modes# without adding a second full extensions layer.RUN for dir in /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} /app/.agent /app/.agents; do \if [ -d "$dir" ]; then \find "$dir" -type d -exec chmod 755 {} +; \find "$dir" -type f -exec chmod 644 {} +; \fi; \done# A2UI bundle may fail under QEMU cross-compilation (e.g. building amd64# on Apple Silicon). CI builds natively per-arch so this is a no-op there.# Stub it so local cross-arch builds still succeed.RUN pnpm canvas:a2ui:bundle || \(echo "A2UI bundle: creating stub (non-fatal)" && \mkdir -p src/canvas-host/a2ui && \echo "/* A2UI bundle unavailable in this build */" > src/canvas-host/a2ui/a2ui.bundle.js && \echo "stub" > src/canvas-host/a2ui/.bundle.hash && \rm -rf vendor/a2ui apps/shared/OpenClawKit/Tools/CanvasA2UI)RUN pnpm build:docker# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)ENV OPENCLAW_PREFER_PNPM=1RUN pnpm ui:build# Prune dev dependencies and strip build-only metadata before copying# runtime assets into the final image.FROM build AS runtime-assetsRUN CI=true pnpm prune --prod && \find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete# ── Runtime base images ─────────────────────────────────────────FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS base-defaultARG OPENCLAW_NODE_BOOKWORM_DIGESTLABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm" \org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_DIGEST}"FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-slimARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGESTLABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-slim" \org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST}"# ── Stage 3: Runtime ────────────────────────────────────────────FROM base-${OPENCLAW_VARIANT}ARG OPENCLAW_VARIANTARG OPENCLAW_BUNDLED_PLUGIN_DIRARG OPENCLAW_DOCKER_APT_UPGRADE# OCI base-image metadata for downstream image consumers.# If you change these annotations, also update:# - docs/install/docker.md ("Base image metadata" section)# - https://docs.openclaw.ai/install/dockerLABEL org.opencontainers.image.source="https://github.com/openclaw/openclaw" \org.opencontainers.image.url="https://openclaw.ai" \org.opencontainers.image.documentation="https://docs.openclaw.ai/install/docker" \org.opencontainers.image.licenses="MIT" \org.opencontainers.image.title="OpenClaw" \org.opencontainers.image.description="OpenClaw gateway and CLI runtime container image"WORKDIR /app# Install system utilities present in bookworm but missing in bookworm-slim.# On the full bookworm image these are already installed (apt-get is a no-op).# Smoke workflows can opt out of distro upgrades to cut repeated CI time while# keeping the default runtime image behavior unchanged.RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \apt-get update && \if [ "${OPENCLAW_DOCKER_APT_UPGRADE}" != "0" ]; then \DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends; \fi && \DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \procps hostname curl git lsof opensslRUN chown node:node /appCOPY --from=runtime-assets --chown=node:node /app/dist ./distCOPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modulesCOPY --from=runtime-assets --chown=node:node /app/package.json .COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR}COPY --from=runtime-assets --chown=node:node /app/skills ./skillsCOPY --from=runtime-assets --chown=node:node /app/docs ./docs# In npm-installed Docker images, prefer the copied source extension tree for# bundled discovery so package metadata that points at source entries stays valid.ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/${OPENCLAW_BUNDLED_PLUGIN_DIR}# Keep pnpm available in the runtime image for container-local workflows.# Use a shared Corepack home so the non-root `node` user does not need a# first-run network fetch when invoking pnpm.ENV COREPACK_HOME=/usr/local/share/corepackRUN install -d -m 0755 "$COREPACK_HOME" && \corepack enable && \for attempt in 1 2 3 4 5; do \if corepack prepare "$(node -p "require('./package.json').packageManager")" --activate; then \break; \fi; \if [ "$attempt" -eq 5 ]; then \exit 1; \fi; \sleep $((attempt * 2)); \done && \chmod -R a+rX "$COREPACK_HOME"# Install additional system packages needed by your skills or extensions.# Example: docker build --build-arg OPENCLAW_DOCKER_APT_PACKAGES="python3 wget" .ARG OPENCLAW_DOCKER_APT_PACKAGES=""RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \if [ -n "$OPENCLAW_DOCKER_APT_PACKAGES" ]; then \apt-get update && \DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $OPENCLAW_DOCKER_APT_PACKAGES; \fi# Optionally install Chromium and Xvfb for browser automation.# Build with: docker build --build-arg OPENCLAW_INSTALL_BROWSER=1 ...# Adds ~300MB but eliminates the 60-90s Playwright install on every container start.# Must run after node_modules COPY so playwright-core is available.ARG OPENCLAW_INSTALL_BROWSER=""RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \if [ -n "$OPENCLAW_INSTALL_BROWSER" ]; then \apt-get update && \DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \mkdir -p /home/node/.cache/ms-playwright && \PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright \node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \chown -R node:node /home/node/.cache/ms-playwright; \fi# Optionally install Docker CLI for sandbox container management.# Build with: docker build --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1 ...# Adds ~50MB. Only the CLI is installed — no Docker daemon.# Required for agents.defaults.sandbox to function in Docker deployments.ARG OPENCLAW_INSTALL_DOCKER_CLI=""ARG OPENCLAW_DOCKER_GPG_FINGERPRINT="9DC858229FC7DD38854AE2D88D81803C0EBFCD88"RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \if [ -n "$OPENCLAW_INSTALL_DOCKER_CLI" ]; then \apt-get update && \DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ca-certificates curl gnupg && \install -m 0755 -d /etc/apt/keyrings && \# Verify Docker apt signing key fingerprint before trusting it as a root key.# Update OPENCLAW_DOCKER_GPG_FINGERPRINT when Docker rotates release keys.curl -fsSL https://download.docker.com/linux/debian/gpg -o /tmp/docker.gpg.asc && \expected_fingerprint="$(printf '%s' "$OPENCLAW_DOCKER_GPG_FINGERPRINT" | tr '[:lower:]' '[:upper:]' | tr -d '[:space:]')" && \actual_fingerprint="$(gpg --batch --show-keys --with-colons /tmp/docker.gpg.asc | awk -F: '1ドル == "fpr" { print toupper(10ドル); exit }')" && \if [ -z "$actual_fingerprint" ] || [ "$actual_fingerprint" != "$expected_fingerprint" ]; then \echo "ERROR: Docker apt key fingerprint mismatch (expected $expected_fingerprint, got ${actual_fingerprint:-<empty>})" >&2; \exit 1; \fi && \gpg --dearmor -o /etc/apt/keyrings/docker.gpg /tmp/docker.gpg.asc && \rm -f /tmp/docker.gpg.asc && \chmod a+r /etc/apt/keyrings/docker.gpg && \printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable\n' \"$(dpkg --print-architecture)" > /etc/apt/sources.list.d/docker.list && \apt-get update && \DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \docker-ce-cli docker-compose-plugin; \fi# Expose the CLI binary without requiring npm global writes as non-root.RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \&& chmod 755 /app/openclaw.mjsENV NODE_ENV=production# Security hardening: Run as non-root user# The node:24-bookworm image includes a 'node' user (uid 1000)# This reduces the attack surface by preventing container escape via root privilegesUSER node# Start gateway server with default config.# Binds to loopback (127.0.0.1) by default for security.## IMPORTANT: With Docker bridge networking (-p 18789:18789), loopback bind# makes the gateway unreachable from the host. Either:# - Use --network host, OR# - Override --bind to "lan" (0.0.0.0) and set auth credentials## Built-in probe endpoints for container health checks:# - GET /healthz (liveness) and GET /readyz (readiness)# - aliases: /health and /ready# For external access from host/ingress, override bind to "lan" and set auth.HEALTHCHECK --interval=3m --timeout=10s --start-period=15s --retries=3 \CMD node -e "fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。