Decision memo: prebuilt image vs. build on demand
The scenario
Your setup from module 01 works: app + db, db reachable by hostname, everything documented. But a new contributor just cloned the repo and is complaining: opening the Dev Container takes several minutes before they can type a single line of code, because Docker has to rebuild the app image from the Dockerfile every time the local cache gets invalidated (a branch switch, a fresh machine, CI running in a cold environment).
Your tech lead asks you directly: should we publish prebuilt devcontainer images to a registry, or keep building on demand on every machine?
There's no universal answer — it depends on team size, how often the Dockerfile changes, and what your CI can already do. Your job: produce a decision memo, not recite a rule.
Task: write your memo before reading on
Before reading the rest of this module, take your module 01 setup (or a real devcontainer.json you have on hand) and do the following, in order:
1. Measure your actual build time.
docker builder prune -f # clear the cache for an honest cold build
time docker build -f .devcontainer/Dockerfile -t bench-app .
Note the real time (the real line from time's output). That's your upper bound — a full cold build, the way a new contributor or an ephemeral, cache-less CI runner would experience it.
2. Estimate what an equivalent docker pull would cost.
You may not have an already-published image to measure a real cold pull against — that's fine. Estimate from image size instead:
docker images bench-app --format "{{.Size}}"
An image in the few-hundred-MB to 1-2 GB range typically downloads in tens of seconds on a decent team connection — far below a multi-minute build that compiles dependencies. Write down your own estimate, with the measured size and a reasonable bandwidth assumption.
3. Write a trade-off table. Three columns minimum: time (cold build vs. pull), storage/registry cost (an image of X GB, how many versions retained, on which registry — GHCR is free for public repos, metered beyond a quota for private ones), and staleness risk (if the image only rebuilds once a week, a contributor may be working against an image slightly out of sync with the current Dockerfile).
4. Decide, in writing, in one or two sentences, with the reasoning that tipped you one way — not just "we prebuild" but "we prebuild because [reason specific to your numbers]".
Do not continue until you have these four things written down, even informally.
Comparison checklist, to self-assess
Here's what containers.dev's official prebuild guide highlights — compare it against your own memo, don't retroactively copy it in.
What prebuilding gets you, per the official docs
- Speed: "pull an already built dev container config rather than having to build it freshly on any new machine."
- Simplicity: the end contributor's
devcontainer.jsoncan shrink to a singleimagefield pointing at the published image — no need for every machine to replay the whole Dockerfile. - Security and stability: pinning a specific version of your tools in the published image "improve[s] supply-chain security and avoid[s] breaks" — a contributor building on demand might unintentionally pick up a newer version of a system dependency at build time, which a pinned image prevents.
Notice what the official docs don't explicitly cover: registry storage cost and staleness risk (an image lagging behind its source Dockerfile) aren't analyzed in the guide — that's a real gap in the documentation, exactly where team judgment has to fill in. If your memo flagged this gap on its own, that's a good sign.
How prebuilding is actually set up
The flow documented by containers.dev:
- Install the official CLI:
npm install -g @devcontainers/cli - Build and publish the image:
devcontainer build --workspace-folder . --push true --image-name <registry>/<namespace>/<name> - Automate this build through a pipeline — GitHub Actions and Azure DevOps are the two officially documented examples — so the image rebuilds on every relevant change (typically: a push touching the Dockerfile or docker-compose.yml), rather than a contributor rebuilding it by hand.
An end contributor's devcontainer.json, once the image is published, can then shrink to:
{
"name": "App + PostgreSQL",
"image": "ghcr.io/your-org/your-repo-app:latest",
"workspaceFolder": "/workspace"
}
(Note this doesn't replace docker-compose.yml for the db service — in a multi-service setup, typically only the application service's image gets prebuilt this way; a database like postgres:16 is already an official published image, so it's already "prebuilt" by nature.)
Decision checklist
Compare your memo against these points — don't look for an exact match, there is no single "correct" answer, only traceable reasoning:
- Did you measure (not eyeball-estimate) your actual cold build time?
- Does your table clearly separate the one-time cost (build time) from the recurring cost (registry storage, maintaining the prebuild pipeline)?
- Did you identify WHO pays the staleness cost if you choose to prebuild — the team remembering to trigger a manual rebuild, or an automated pipeline triggered on push?
- Does your decision cite a number or threshold ("if the build exceeds X minutes", "if the team exceeds X contributors") rather than a general preference?
- Did you note what breaks if the prebuild pipeline itself goes down — a
devcontainer.jsonreduced toimagewith no localbuildfallback makes the whole team dependent on registry availability.
If your memo hits these five points, you've produced a real engineering decision, not an opinion.
What's next
The next module changes topic: we leave container architecture behind to make sure your local pre-commit hooks are actually enforced in CI, not just on your machine.
Check your understanding
According to the official containers.dev prebuild guide, which benefit is NOT explicitly highlighted?
A prebuild pipeline that pushes a new image on every merge to main automatically solves which risk, but introduces which one?
If the team chooses prebuild WITHOUT an automated CI pipeline (a maintainer reruns the build by hand now and then), who "pays" the cost when the image goes stale?
Want to hear about the next modules?
The Academy stays free and open-access, no sign-up required. If you'd just like to be notified by email when a new module ships, here you go — no obligation, unsubscribe anytime with one click.