Skip to content
Latchkey

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

Run this faster and cheaper on Latchkey managed runners. Start free →