How to Reduce Docker Image Size With a Multi-Stage Build in CI
A multi-stage build keeps compilers and dev dependencies out of the final image by copying only the built output forward.
Compile in a heavier builder stage, then COPY --from=builder only the runtime artifacts into a minimal base. The toolchain layers are discarded, shrinking the published image and its attack surface.
Steps
- Name the build stage with
FROM ... AS builder. - Install dependencies and build in that stage.
- Start a slim final stage and
COPY --from=builderonly what runtime needs.
Dockerfile
Dockerfile
FROM node:20-bookworm AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/server.js"]Gotchas
- Only the final stage is published; earlier stages exist only to feed
COPY --from. - A
-slimor distroless base trims the runtime further, but verify required shared libraries are present.
Related guides
How to Pin a Docker Base Image by Digest in CIMake Docker builds reproducible in CI by pinning the FROM base image to an immutable sha256 digest instead of…
How to Cache Docker Layers With a Registry Cache in CIPersist Docker build layers across CI runs with Buildx registry cache (type=registry), pushing and pulling ca…