skip to content

In `docker buildx build`, what does `--provenance=mode=max` record that `mode=min` does not?

level: seniorimportance: should knowfreq 44%

answer

  1. Two settings on the same provenance flag
  2. One is a superset of the other
  3. The richer one describes the build's interior
  4. Build argument values travel with it
  5. Prefer secret mounts over build args

basics

~10 s

mode=min keeps the skeleton: builder identity, timestamps, source reference and output digest. mode=max adds the full build definition — Dockerfile, per-step metadata, resolved inputs, and every --build-arg value, which is how secrets leak.

solid answer

~50 s

Both modes produce a provenance attestation about the same image; they differ in how much of the build they describe. `mode=min` records the minimum that identifies the build: which builder ran it, when it started and finished, which frontend was used, the source context reference (for a git context, the remote and revision), and the digest of what came out. `mode=max` additionally carries the full build definition — the Dockerfile source and per-step metadata, the resolved digests of the inputs the build consumed, and **the build arguments with their values**. That last item makes `max` a judgement call: a `--build-arg` holding a token ends up as readable JSON next to your image, and on a public repository that is a disclosure. Keep credentials in `RUN --mount=type=secret` instead, and default to `min` for anything published outside your own registry.

code

bash · 8 lines
bash
# rich record, private registry, no credentials in build args
docker buildx build --provenance=mode=max \
  --secret id=composer_auth,src=$HOME/.composer/auth.json \
  -t registry.internal/billing/invoice-worker:2.7.3 --push .

# skeleton record for an externally published image
docker buildx build --provenance=mode=min \
  -t docker.io/example/invoice-worker:2.7.3 --push .

go deeper

for a junior

Know that provenance has two levels of detail and that the flag is --provenance=mode=min or mode=max on docker buildx build. Recalling that max includes far more about the build is enough here.

for a middle

Explain the mechanics: min is the identifying skeleton — builder, timestamps, source reference, output digest — and max adds the Dockerfile, per-step metadata, resolved inputs and build-arg values.

for a senior

Show the production judgement. Name the disclosure risk of max on a widely readable repository, argue for secret mounts over build args, and set the mode explicitly per pipeline rather than inheriting a default.

for a principal

Own the policy: where in the estate max is worth its disclosure surface, how you audit build arguments before flipping it, and why richer provenance is not stronger provenance without signing and an enforcement point.

### The same claim at two resolutions A provenance attestation answers "how did these bytes come to exist?". BuildKit lets you choose how much of the answer to publish, because the answer is written by the build and stored next to the image where anyone who can pull the image can read it. **`mode=min`** is the skeleton. It records the things that identify the build without describing its interior: the builder that ran it, the build's start and finish timestamps, the frontend in use, the source context reference — for a git-URL context, the remote and the revision that was checked out — and the subject digest of the image produced. **`mode=max`** is a superset. On top of everything above it carries the full build definition: the Dockerfile source itself, per-step metadata for the build graph, the resolved digests of the inputs the build actually consumed, and the complete set of build arguments **with their values**. ### Why `max` is not simply better The extra fidelity is genuinely useful. With `max` you can answer, months later, which base image digest a release was built on and what the Dockerfile said at the time, from the artefact alone, without trusting that a git tag still points where it did. That is exactly the question that arrives during an incident. The cost is disclosure. Build arguments are a documented, everyday way of parameterising a build, and plenty of pipelines pass things through them that should never have been there — a registry token, a package-index URL with credentials embedded, an internal hostname. With `mode=min` those values are simply absent from the record. With `mode=max` they are in a JSON blob a `docker buildx imagetools inspect` away, for anyone who can read the repository. Concretely: an invoice-rendering worker built from a PHP-FPM base takes a `--build-arg COMPOSER_AUTH` so that a private package repository will answer. Switch that pipeline from the default minimal provenance to `mode=max` and push to a repository that is readable organisation-wide, and the credential is now published — not in a layer, where a scanner or a review might have caught it, but in metadata almost nobody looks at. The defence is not "use `min`". It is to stop putting secrets in build arguments at all: ```dockerfile # syntax=docker/dockerfile:1 FROM php:8.3-fpm AS deps RUN --mount=type=secret,id=composer_auth,target=/root/.composer/auth.json \ composer install --no-dev --prefer-dist ``` A build secret mounted this way is present only for that one `RUN`, never lands in a layer, and is referenced in provenance by its id rather than its value. Once credentials are handled that way, `mode=max` becomes a much easier call. ### Size and noise `max` provenance is larger — the build definition for a multi-stage build is not small — and it is produced per platform, so a two-architecture image carries two of them. On its own this is rarely decisive; registry storage is cheap. It matters more when you multiply by thousands of builds a week with short retention windows, and when the extra index entries interact with tooling that did not expect an image reference to resolve to something with unknown-platform members. ### Choosing per pipeline A defensible position for most organisations: - **Internal images, private registry, no secrets in build args:** `mode=max`. The reconstruction value is real and the audience is bounded. - **Anything published externally or to a widely readable repository:** `mode=min` unless you have specifically audited what the build arguments contain. - **Third-party or vendor builds you consume:** you do not choose; you read what is there and note what is missing. And be explicit rather than relying on defaults. Recent Buildx versions attach `mode=min` provenance automatically when a build pushes to a registry, which means some teams have provenance they never decided to have and others have deleted it wholesale with `BUILDX_NO_DEFAULT_ATTESTATIONS=1` because an old tool choked on the index. Writing the flag into the pipeline makes the decision visible in review. ### What neither mode does Neither mode signs anything. Both write a record that the build asserts about itself, stored in the same registry as the image, writable by anyone who can push there. `mode=max` is a richer statement, not a more trustworthy one; increasing the detail of an unsigned claim does not make it harder to forge. What provenance adds over a bare signature is the *content* of the claim — which source revision, which builder, which inputs — rather than any additional assurance about who made it.

  • What does a provenance claim prove that a signature alone does not?
    A signature says an identity vouched for these exact bytes. Provenance says what produced them — which source revision, which builder, which inputs, at what time — so a consumer can check the image came from the branch and build system it was supposed to, not merely that a trusted key touched it. They are complementary: the signature covers who asserts, the provenance covers what is asserted.
  • A build passes a token as `--build-arg` and the image is already pushed with `mode=max`. What do you do?
    Treat the token as disclosed and rotate it first — deleting the tag does not un-publish something already pulled or cached. Then move the credential to a `RUN --mount=type=secret`, rebuild, and re-push. Finally add a pipeline check that fails when a build-arg name matches a secret-shaped pattern, because this recurs the moment someone copies the old pipeline.
  • Does `mode=max` make the provenance harder to forge?
    No. Both modes produce an unsigned record stored in the same repository as the image, so anyone with push rights can replace it. `max` gives a richer statement, not a more trustworthy one — trustworthiness comes from who signs the attestation and from a consumer that actually checks it.

saying these in an interview costs you the question

  • Thinks `mode=max` is strictly better and should always be on
  • Unaware that build-arg values appear in max provenance
  • Claims provenance is signed and therefore tamper-proof
  • Confuses a build secret mount with a `--build-arg`
  • Says the difference is only the size of the record

context