Where Image Size Comes From
Where Image Size Comes From
An image is a stack of layers; each instruction adds one. Size concentrates in the base OS, installed packages, and copied artifacts:
docker history app:1.4 --human # size attributed per layer
Two fixes dominate. First, COPY respects .dockerignore — excluding .git, node_modules, and test fixtures stops them from entering a layer at all. Second, order instructions so volatile files come last: package manifests, then install, then source. Base choice matters too — a full node:20 carries toolchains you never run in production, while node:20-slim or -alpine trims that immediately.
Multi-Stage and Distroless
Multi-Stage and Distroless
Compile in one stage, ship only the artifact:
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=build /app/dist ./dist
CMD ["dist/server.js"]
Distroless images contain the runtime and its certificates but no shell, package manager, or userland — smaller attack surface, and docker exec debugging is impossible by design (use docker debug or a sidecar when needed). Alpine is the pragmatic middle: small and musl-based, but glibc-linked binaries need compatibility shims and native modules must compile against musl.
Pitfalls and Interview Angles
Pitfalls and Interview Angles
Copying node_modules or __pycache__. Host artifacts bloat layers and often mismatch the image architecture — install inside the build stage only.
Missing runtime deps in distroless. tzdata, ca-certificates, and timezone config are absent or minimal; test TLS and Intl behavior in the target image, not the build stage.
No measurement. docker system df -v and dive show layer-by-layer waste; add a size check to CI so a 400 MB regression fails the build.
Interview: give your base-image decision ladder, walk a multi-stage Dockerfile, and name one debugging cost of distroless you accept in exchange for the size.
Quiz
1. What is the main purpose of .dockerignore?
2. What do you lose with a distroless runtime image?
3. Why can host-copied node_modules break an image?
Flashcards
Question
Layer attribution
Click to reveal answer
Answer
docker history --human shows which instruction added how many megabytes.
Question
Multi-stage build
Click to reveal answer
Answer
Toolchain in builder stage; final stage copies only artifacts via COPY --from.
Question
Distroless
Click to reveal answer
Answer
Runtime-only base: no shell or package manager, smaller surface, harder to debug.
Question
Alpine tradeoff
Click to reveal answer
Answer
Small musl-based image; glibc binaries and native modules need rebuilds or shims.
Question
.dockerignore
Click to reveal answer
Answer
Keeps .git, node_modules, fixtures out of the build context entirely.
Revision Notes
Key Takeaways
- 1. Measure first with docker history and dive, then cut
- 2. Multi-stage plus a lean base removes most weight
- 3. Install dependencies inside the image, never copy host artifacts
Interview Tips
- • Walk your Dockerfile and attribute megabytes per layer
- • Defend base choice (slim vs alpine vs distroless) against a debugging objection
Cheat Sheet
history/dive to measure; .dockerignore first; multi-stage COPY --from=build; slim/alpine/distroless ladder; install deps in-image; CI size budget.
Continue Learning
Before You Start
- No prerequisites listed.