Measuring Size

Measuring the Size Reduction with docker history

docker history lists each layer with its size and the instruction that made it, so it shows what a stage added:

The dependency layer of each imageShell
F='{{.Size}}\t{{.CreatedBy}}'
docker history l3-booknest-api:1.0 --format "$F" | sed -n 6p | cut -c1-60
docker history l3-booknest-api:1.1 --format "$F" | sed -n 8p | cut -c1-60
Output
8.26MB  RUN |1 NODE_ENV=production /bin/sh -c npm ci…
5.51MB  COPY /app/node_modules ./node_modules # buil…

Of the single-stage npm 2,036 ci layer's 8.26 MB, only 5.51 MB is node_modules; the rest is npm's leftovers. A runtime stage on gcr.io/distroless/nodejs24-debian13 (its healthcheck calling /nodejs/bin/node, which is outside PATH there) shows how much the base image still decides:

BookNest's API image four ways, measured with docker image ls
Image Disk usage Download Shell and npm
Single stage on node:24 1.64 GB 411 MB Yes, plus GCC
Single stage on node:24-slim (BookNest Dockerfile) 339 MB 82.1 MB Yes
Multi-stage, node:24-slim runtime 336 MB 81.6 MB Yes
Multi-stage, distroless runtime 221 MB 56.1 MB No

The distroless variant turned healthy and served the API, but docker exec into it fails with exec: "sh": executable file not found, so BookNest keeps node:24-slim and its shell (docker debug debugs distroless). That base is 332 MB of the 336: the real gains here are the test target and a runtime without extras.