awesome-everything RU
↑ Back to the climb

Deployment & Infra

Image layers: optimise a real Dockerfile

Crux Hands-on project — take a slow, bloated, leaky Dockerfile and rewrite it for cache reuse, image size, and secret safety, proving every step with before/after numbers.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 210 min

Reading about cache prefixes and multi-stage builds is not the same as making a four-minute CI build into a twenty-second one. Take a deliberately bad Dockerfile, drive it into all three failure modes — cache thrash, image bloat, and a leaked secret — then rewrite it and prove each fix with measurements.

Goal

Turn the unit’s mental model into a reproducible engineering loop: measure a baseline build, reorder for the cache prefix, split it multi-stage to ship slim, filter the build context, eliminate a secret from layer history, and verify every change with before/after numbers.

Project
0 of 7
Objective

Take a deliberately bad Dockerfile (your own app or the starter described below) and rewrite it so a routine source edit rebuilds in seconds instead of minutes, the published image drops by at least half, and no secret survives in layer history — proving each step with measurements, not claims.

Requirements
Acceptance criteria
  • A before/after table: cold build time, warm-rebuild-after-one-edit time, final image size, and transferred build-context size — measured, not estimated.
  • The warm rebuild after a one-line source edit no longer re-runs the dependency install (visible as 'CACHED' in build output), and the final image is at least 50% smaller than the single-stage baseline.
  • docker history (or layer extraction) on the final image shows no recoverable token and no .env file in any layer.
  • A short write-up mapping each change to the mechanism it exploits: cache prefix, RUN-text caching, multi-stage discard, build-context filtering, and immutable-layer secret safety.
Senior stretch
  • Add a CI cache: enable BuildKit registry or GitHub Actions cache (--cache-from / --cache-to) so the warm-rebuild win survives across ephemeral CI runners, and show the install layer hitting cache on a fresh runner.
  • Add a size-and-secret CI gate: fail the build if the final image grows beyond a threshold, and run a secret scanner (e.g. trivy or dockle) over the image layers so a re-introduced secret breaks the pipeline.
  • Repeat the multi-stage exercise for a second language (a Go binary on scratch/distroless/static and a Node app on distroless) and compare how small each runtime can get and why.
  • Pin the base image by digest and add a renovate/dependabot rule, then show how a digest change invalidates exactly the expected prefix of layers and nothing earlier.
Recap

This is the loop you will run on every real Dockerfile: measure first, reorder for the cache prefix so the expensive install stays a hit, merge update-and-install, go multi-stage to ship slim, filter the context with .dockerignore, and get secrets out of layer history with a secret mount or a discarded builder stage — verifying each change with before/after numbers under the same conditions. Doing it once on a deliberately bad Dockerfile makes the production rewrite muscle memory: order, slim, ignore, and never rm a secret.

Continue the climb ↑Compose vs Kubernetes: choosing the right orchestration weight
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.