From 826ddaa9aabf551641d6f6550734fd9bb74f7e1e Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Tue, 1 Sep 2026 21:54:56 +0200 Subject: [PATCH] build(docker): ship the Docker CLI in the Compose image, not the whole engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker/server.Dockerfile installed Debian's `docker.io` to get a client for the socket mounted by Compose. That package is the full ENGINE: even with --no-install-recommends it pulls 15 packages including containerd, runc, dmsetup and iptables, none of which a container that only talks to a mounted socket can use, and it ships Docker 20.10.24 (2023). Copy the CLI and the buildx plugin from the official docker:29-cli image instead. Measured on the same node:22-bookworm-slim base: 266 MB -> 108 MB, so 158 MB smaller with a current CLI (29.7.2) in place of a two-year-old one. Three things verified rather than assumed, by building the real image and running it: - docker:cli is an ALPINE image, so copying a binary into this Debian one is only safe because the binaries are static Go builds (ldd: "Not a valid dynamic program"). In the built image, `docker --version`, `docker ps` and `docker build` all work against a mounted host socket as the unprivileged runtime user. - buildx is copied on purpose. scripts/build-agent-image.mjs shells out to `docker build` and Codeman auto-builds the agent image on the first Docker case. Without the plugin that still works today — CLI 29 falls back to the classic builder, tested — but that builder is deprecated and will be dropped, so the plugin keeps the path supported. - docker-compose is NOT copied: Codeman never shells out to it. Pinned to the 29 major, matching how the base images here are pinned. --- docker/server.Dockerfile | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index bc6aaca7..d346866d 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -29,7 +29,6 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ - docker.io \ git \ openssh-client \ procps \ @@ -37,6 +36,27 @@ RUN apt-get update \ tmux \ && rm -rf /var/lib/apt/lists/* +# The Docker CLI, taken from the official image rather than Debian's `docker.io`. +# That package is the full ENGINE: with --no-install-recommends it still pulls 15 +# packages including containerd, runc, dmsetup and iptables, none of which a +# client that only talks to a mounted socket can use. Measured on top of this +# base image: `docker.io` costs 266 MB and ships Docker 20.10.24 (2023), while +# these two files cost 108 MB and ship the current CLI (493 MB vs 335 MB total). +# +# The binaries are STATIC Go builds, so they run on this glibc image even though +# the image they come from is Alpine (verified: `docker --version`, `docker ps` +# and `docker build` all work here against a mounted host socket). +# +# buildx is copied on purpose. `scripts/build-agent-image.mjs` shells out to +# `docker build` — Codeman auto-builds the agent image on the first Docker case — +# and without the plugin that silently falls back to the CLASSIC builder, which +# Docker has deprecated and will eventually drop. `docker-compose` is NOT copied: +# Codeman never shells out to it. +COPY --from=docker:29-cli /usr/local/bin/docker /usr/local/bin/docker +COPY --from=docker:29-cli \ + /usr/local/libexec/docker/cli-plugins/docker-buildx \ + /usr/local/libexec/docker/cli-plugins/docker-buildx + # Keep credentials out of the image. Users authenticate these CLIs at runtime # through Codeman sessions, and the configured host bind mount retains state. RUN npm install --global \