Safeguard
Developer Tools•

How do you generate an SBOM for a multi-stage Docker build?

Docker's BuildKit SBOM covers only the final stage by default. How to include earlier stages or the build context, and what to inspect before you push.

Safeguard Security Team
3 min read

With Docker BuildKit you generate an SBOM by adding the SBOM attestation option to your build. In a multi stage build, only the final stage is scanned by default, so build time tools from earlier stages do not appear. Opt in with Dockerfile arguments if you want those stages or the build context included.

The basic command

docker buildx build --sbom=true --push -t registry.example.com/app:1.5.0 .

That is shorthand for an attestation of type sbom. The default scanner is the BuildKit Syft scanner plugin, built on Syft. You can replace it with another image that implements the BuildKit SBOM scanner protocol.

Which stage is described

SettingEffect
DefaultFinal stage only
Stage scan argument set to true in a stageThat stage is scanned too
Stage scan argument as a list of namesThose stages are scanned, if they are built
Context scan argument set to trueThe build context is included

Two details trip people up. Both arguments must be declared with ARG in the Dockerfile, since passing the flag only on the command line has no effect. And environment variables and variable substitution do not work for them.

FROM golang:1.24 AS build
ARG BUILDKIT_SBOM_SCAN_STAGE=true
RUN go build -o /app ./cmd/server

FROM gcr.io/distroless/static
COPY --from=build /app /app

Should you include the build stage?

It depends on the question you are answering.

  1. What is running in production? Final stage only. This is the default and what most buyers want.
  2. What did our build depend on? Include the build stage. This helps incident response if a compiler or build tool is later found compromised.
  3. What did we copy in from the repo? Include the context.

Many teams keep two files: the production SBOM they share, and a fuller one they keep internally.

Check before you push

Build with the local output option to write files to a folder instead of an image. You will get sbom.spdx.json, and one extra file per scanned stage named for the stage. Open it and confirm a few packages you expect.

After pushing, inspect with docker buildx imagetools inspect and a format template. The SBOM data is under the SBOM key, per platform if the image is multi platform.

What the attestation looks like

The output is a JSON encoded SPDX document wrapped in an in toto statement, in the SLSA style. If you copy files into the final image from a stage, an SBOM of the final stage alone may only see them as files, not as packages. For compiled languages, a tool that reads embedded build data from the binary can help.

Try it yourself

Click through it at your own pace, or open it full screen.

Look at how packages are grouped by layer and image, and think about which of those layers your SBOM should describe.

Quick check

Why are my compiler and build tools missing from the SBOM?

They live in an earlier stage, and only the final stage is scanned unless you opt in.

Can I set the stage scan option with a shell variable?

No. It must be an ARG in the Dockerfile. Variable substitution does not work for it.

Safeguard's SBOM and dependency scanning includes container image scanning, and a free scan is available at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.