From 2cd23957c06adcff7c002d8c44ec739aaa23365f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 22:47:44 +0100 Subject: [PATCH] build: use slim docker runtime --- .github/workflows/docker-release.yml | 83 ++-------------------------- Dockerfile | 26 +++------ docs/install/docker.md | 4 +- scripts/e2e/Dockerfile | 2 +- scripts/e2e/Dockerfile.qr-import | 2 +- src/dockerfile.test.ts | 9 +-- 6 files changed, 23 insertions(+), 103 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 3bc355f0dba..93806f47fb1 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -63,7 +63,7 @@ jobs: # KEEP THIS WORKFLOW ON GITHUB-HOSTED RUNNERS. # DO NOT MOVE IT BACK TO BLACKSMITH WITHOUT RE-VALIDATING TAG BUILDS AND BACKFILLS. - # Build amd64 images (default + slim share the build stage cache) + # Build amd64 image. Default and slim tags point to the same slim runtime. build-amd64: needs: [approve_manual_backfill] if: ${{ always() && (github.event_name != 'workflow_dispatch' || needs.approve_manual_backfill.result == 'success') }} @@ -74,7 +74,6 @@ jobs: contents: read outputs: digest: ${{ steps.build.outputs.digest }} - slim-digest: ${{ steps.build-slim.outputs.digest }} steps: - name: Checkout uses: actions/checkout@v6 @@ -117,12 +116,7 @@ jobs: fi { echo "value<> "$GITHUB_OUTPUT" - { - echo "slim<> "$GITHUB_OUTPUT" @@ -167,25 +161,7 @@ jobs: provenance: mode=max push: true - - name: Build and push amd64 slim image - id: build-slim - # WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY. - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - platforms: linux/amd64 - cache-from: type=gha,scope=docker-release-amd64 - cache-to: type=gha,mode=max,scope=docker-release-amd64 - build-args: | - OPENCLAW_EXTENSIONS=diagnostics-otel - OPENCLAW_VARIANT=slim - tags: ${{ steps.tags.outputs.slim }} - labels: ${{ steps.labels.outputs.value }} - sbom: true - provenance: mode=max - push: true - - # Build arm64 images (default + slim share the build stage cache) + # Build arm64 image. Default and slim tags point to the same slim runtime. build-arm64: needs: [approve_manual_backfill] if: ${{ always() && (github.event_name != 'workflow_dispatch' || needs.approve_manual_backfill.result == 'success') }} @@ -196,7 +172,6 @@ jobs: contents: read outputs: digest: ${{ steps.build.outputs.digest }} - slim-digest: ${{ steps.build-slim.outputs.digest }} steps: - name: Checkout uses: actions/checkout@v6 @@ -239,12 +214,7 @@ jobs: fi { echo "value<> "$GITHUB_OUTPUT" - { - echo "slim<> "$GITHUB_OUTPUT" @@ -289,24 +259,6 @@ jobs: provenance: mode=max push: true - - name: Build and push arm64 slim image - id: build-slim - # WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY. - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - platforms: linux/arm64 - cache-from: type=gha,scope=docker-release-arm64 - cache-to: type=gha,mode=max,scope=docker-release-arm64 - build-args: | - OPENCLAW_EXTENSIONS=diagnostics-otel - OPENCLAW_VARIANT=slim - tags: ${{ steps.tags.outputs.slim }} - labels: ${{ steps.labels.outputs.value }} - sbom: true - provenance: mode=max - push: true - # Create multi-platform manifests create-manifest: needs: [approve_manual_backfill, build-amd64, build-arm64] @@ -361,16 +313,11 @@ jobs: fi { echo "value<> "$GITHUB_OUTPUT" - { - echo "slim<> "$GITHUB_OUTPUT" - - name: Create and push default manifest + - name: Create and push manifest shell: bash env: TAGS: ${{ steps.tags.outputs.value }} @@ -388,24 +335,6 @@ jobs: "${AMD64_DIGEST}" \ "${ARM64_DIGEST}" - - name: Create and push slim manifest - shell: bash - env: - SLIM_TAGS: ${{ steps.tags.outputs.slim }} - AMD64_SLIM_DIGEST: ${{ needs.build-amd64.outputs.slim-digest }} - ARM64_SLIM_DIGEST: ${{ needs.build-arm64.outputs.slim-digest }} - run: | - set -euo pipefail - mapfile -t tags <<< "${SLIM_TAGS}" - args=() - for tag in "${tags[@]}"; do - [ -z "$tag" ] && continue - args+=("-t" "$tag") - done - docker buildx imagetools create "${args[@]}" \ - "${AMD64_SLIM_DIGEST}" \ - "${ARM64_SLIM_DIGEST}" - verify-attestations: needs: [create-manifest] if: ${{ always() && needs.create-manifest.result == 'success' }} diff --git a/Dockerfile b/Dockerfile index e5e0db2082a..fa44b115df8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,22 +9,19 @@ # 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 . +# Build stages use full bookworm; the runtime image is always bookworm-slim. ARG OPENCLAW_EXTENSIONS="" -ARG OPENCLAW_VARIANT=default ARG OPENCLAW_BUNDLED_PLUGIN_DIR=extensions ARG 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. # Dependabot refreshes these blessed digests; release builds consume the # reviewed base snapshot instead of mutating distro state on every build. -# 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. +# To update, run: docker buildx imagetools inspect node:24-bookworm and +# node:24-bookworm-slim (or podman) and replace the digests below with the +# current multi-arch manifest list entries. FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps ARG OPENCLAW_EXTENSIONS @@ -125,20 +122,14 @@ RUN printf 'packages:\n - .\n - ui\n' > /tmp/pnpm-workspace.runtime.yaml && \ node scripts/postinstall-bundled-plugins.mjs && \ 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-default -ARG OPENCLAW_NODE_BOOKWORM_DIGEST -LABEL 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-slim +# ── Runtime base image ────────────────────────────────────────── +FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-runtime ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST LABEL 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_VARIANT +FROM base-runtime ARG OPENCLAW_BUNDLED_PLUGIN_DIR # OCI base-image metadata for downstream image consumers. @@ -154,8 +145,7 @@ LABEL org.opencontainers.image.source="https://github.com/openclaw/openclaw" \ 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). +# Install runtime system utilities missing from bookworm-slim. 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 && \ diff --git a/docs/install/docker.md b/docs/install/docker.md index ad3452d074b..22bda7c123a 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -357,8 +357,8 @@ See [ClawDock](/install/clawdock) for the full helper guide. - The main Docker image uses `node:24-bookworm` and publishes OCI base-image - annotations including `org.opencontainers.image.base.name`, + The main Docker runtime image uses `node:24-bookworm-slim` and publishes OCI + base-image annotations including `org.opencontainers.image.base.name`, `org.opencontainers.image.source`, and others. The Node base digest is refreshed through Dependabot Docker base-image PRs; release builds do not run a distro upgrade layer. See diff --git a/scripts/e2e/Dockerfile b/scripts/e2e/Dockerfile index 91bbcffcd1b..4fcf14ac6fe 100644 --- a/scripts/e2e/Dockerfile +++ b/scripts/e2e/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b AS e2e-runner +FROM node:24-bookworm-slim@sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb AS e2e-runner RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates git \ diff --git a/scripts/e2e/Dockerfile.qr-import b/scripts/e2e/Dockerfile.qr-import index 0d7be4798af..16bbf189787 100644 --- a/scripts/e2e/Dockerfile.qr-import +++ b/scripts/e2e/Dockerfile.qr-import @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b +FROM node:24-bookworm-slim@sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb RUN corepack enable diff --git a/src/dockerfile.test.ts b/src/dockerfile.test.ts index b6f0f0e5956..c140f2128a1 100644 --- a/src/dockerfile.test.ts +++ b/src/dockerfile.test.ts @@ -13,7 +13,7 @@ function collapseDockerContinuations(dockerfile: string): string { } describe("Dockerfile", () => { - it("uses shared multi-arch base image refs for all root Node stages", async () => { + it("uses full bookworm for build stages and slim bookworm for runtime", async () => { const dockerfile = await readFile(dockerfilePath, "utf8"); expect(dockerfile).toContain( 'ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"', @@ -23,10 +23,11 @@ describe("Dockerfile", () => { ); expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps"); expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build"); - expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS base-default"); - expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-slim"); - expect(dockerfile).toContain("current multi-arch manifest list entry"); + expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-runtime"); + expect(dockerfile).toContain("FROM base-runtime"); + expect(dockerfile).toContain("current multi-arch manifest list entries"); expect(dockerfile).not.toContain("current amd64 entry"); + expect(dockerfile).not.toContain("OPENCLAW_VARIANT"); }); it("installs optional browser dependencies after pnpm install", async () => {