Traditional multi-stage Dockerfiles are extremely powerful but become difficult to reason about at scale:
- Linear file order + implicit dependencies via
FROM foo/COPY --from=foo. - Lots of boilerplate for “download in parallel for cache” patterns (see the giant
torch/Dockerfileandtorch-extras/Dockerfilein coreweave/ml-containers). - Scripts must be
COPYed (or put in heredocs) even when you only want them for the build. - Secret mounts are verbose and easy to get slightly wrong (leaking into layers or logs).
- Hard to express “these N independent things can be built in parallel and then combined”.
Yamlfile makes the graph explicit:
targetsare named first-class citizens.from:andcopy.from:(sibling targets today; thecomponent:targetform for multi-file references is accepted but not yet supported at runtime) declare dependencies.- Only the targets you actually need are built.
run.scriptlets you run a script from your build context without leaving a copy of it in the final image.- Secrets have a clean declarative form that maps directly to
type=secret(file orenv=).
You still get the full power of BuildKit (caching, multi-platform, provenance, etc.).
See the Syntax Reference for how targets, dependencies, and steps are declared.