How to Build Smaller Docker Images to Cut CI Transfer
Every job that pulls a one-gigabyte image moves that gigabyte again; a slim image cuts the transfer each time.
Large images are pulled, pushed, cached, and stored on every run, and that data movement costs bandwidth, storage, and energy across a whole fleet. Multi-stage builds and slim bases ship only what runs.
Steps
- Use a multi-stage build so build tools never reach the final image.
- Start the runtime stage from a slim or distroless base.
- Copy only the built artifacts into the final stage.
Dockerfile
Dockerfile
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-slim AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]Tradeoffs
- Distroless images lack a shell, which complicates debugging inside the container.
- Order layers so the least-changing ones cache; a good cache multiplies the transfer savings.
Related guides
How to Reduce Data Egress in CI PipelinesCut the data CI moves across networks by co-locating caches and registries, shallow-cloning, and pruning down…
How to Cache Builds to Avoid Recomputing in CICache dependency stores and build outputs in GitHub Actions so CI stops rebuilding unchanged work every run,…