Charlie KrugThe Build Log

← All posts

Docker's cache is a chain. One stale link rebuilds the rest.

Layerlens models a Docker build without running it, maps every instruction to a layer, and shows exactly which layers a one-character source edit forces to rebuild, plus the reorder that keeps them cached.

Change one character in a source file, rerun docker build, and watch it reinstall three hundred npm packages you never touched. The Dockerfile works. It just quietly re-downloads half the internet on every commit, and the only signal you get is that builds "feel slow." The reason is sitting in plain text on line 4, and nothing on your machine points at it.

What it is

Layerlens reads a Dockerfile the way the Docker daemon does, one instruction and one layer at a time, and shows you a visual stack of those layers: how heavy each one is, and which ones a routine source edit will force to rebuild. It names the exact line that's costing you and the exact reorder that fixes it. It never runs docker build, never needs a daemon, and never uploads your Dockerfile anywhere. Everything happens on pasted text in the tab.

The whole thing exists because Docker's layer cache is the single biggest lever on build speed and image size, and it is almost completely invisible. You write a Dockerfile top to bottom, it runs, and you never see that a COPY . . above your install throws away the install on every edit.

The cache is a chain, not a set

Here is the model worth carrying around even if you never open the app. Each instruction in a Dockerfile becomes a layer, and each layer has a cache key derived from its own contents and every layer before it. So the cache is not a bag of independently-reusable pieces. It's a chain. The moment one link's key changes, its cache is busted, and because every downstream key was computed on top of it, every layer below it is busted too. Invalidation only ever cascades downward, and it never stops early.

That single rule explains the classic mistake:

FROM node:18-slim
WORKDIR /app
COPY . .
RUN npm ci --production

COPY . . on line 3 hashes your entire source tree, so a one-character edit changes its key. RUN npm ci sits below it, so its key is downstream, so it re-runs the full dependency install every single time. Copy the manifest first, install, then copy the rest, and the install layer's key stops depending on your source. It stays cached across code changes. Same instructions, same image, but the chain is now ordered so the expensive link sits above the volatile one.

Modeling the cascade without a daemon

Layerlens computes all of this statically. It parses the Dockerfile into an instruction stream, maps each instruction to a modeled layer with a relative size weight estimated from its semantics, then walks the chain to work out which keys a given change invalidates. Hover any layer and it sweeps down every layer that layer's change would take with it, and the headline "rebuilds on a source edit" percentage updates to match. You watch the cascade instead of guessing at it.

Doing that without executing the build is the actual engineering. You have to genuinely understand the build model: line continuations and the escape directive, comments living inside a continuation, which instructions even create layers, what busts a cache key versus what doesn't, and multi-stage boundaries where a FROM ... AS build and a later COPY --from=build mean a change in one stage cascades into another. Get any of that wrong and the cascade you draw is a lie. The parser and layer model are a dependency-free, unit-tested core with the UI as a thin renderer on top, precisely because that reasoning is the product.

One thing Layerlens deliberately refuses to do is claim byte sizes. Real megabytes need a real build with your real context; anything else is a guess dressed up as a fact. So it reports relative weights and clearly-labeled heuristics instead: enough to rank fixes honestly and see where the fat is, without lying about a number it can't know. A clean, well-ordered Dockerfile produces no suggestions at all. The tool stays quiet when there's nothing to say.

Try it

Open Layerlens and you land in a two-pane workbench seeded with a sample Dockerfile. Paste your own over it, or load one of the Node, Python, or Go multi-stage examples, and the layer stack on the right re-renders live. Hover the layers top to bottom and find the first one that lights up half the stack below it: that's your cache-busting line. Then read the ranked suggestions docked under the stack, which name the offending line and the precise change, and go make your next build a cache hit.

Layerlens is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…