SBOM tools compared: Syft, Trivy, docker sbom, and cluster-level SBOMs
How to generate an SBOM with Syft, Trivy, docker sbom, cdxgen and Docker Scout, what each is best at, and why a cluster-level SBOM of what is running is a different tool from any of them.
StackRadar Team
· 10 min read
There are more SBOM generators than there are good reasons to choose between them. For container images the field has effectively settled — Syft, Trivy, and the tools that wrap one or the other — and the interesting choices are elsewhere: build-time versus image-time, and single image versus everything a cluster runs. This post covers the generators you will actually meet, credits the one we built on, and explains why a cluster-level SBOM is a different tool from all of them.
Syft
Syft (Anchore, Apache-2.0) is the tool the rest of this post is measured against. It reads an image, a directory or a file, runs a set of catalogers — apk, dpkg, rpm, npm, pip, Go modules, Java archives, Rust, Ruby, PHP, .NET, binaries and more — and emits CycloneDX, SPDX or its own JSON.
syft registry:docker.io/library/nginx:1.27-alpine --platform linux/amd64 \
-o cyclonedx-json@1.6 > nginx.cdx.jsonThat exact command produced the downloadable example on our CycloneDX page: 68 Alpine packages, one operating-system component, 62 dependency edges, and 978 file entries recording binary evidence. It is also, with no changes, the generator inside the StackRadar scanner — we did not write our own, because Syft is better than anything we would have written and its output is what every matcher already understands. Credit where due: Anchore maintains it in the open, and it is the reason the SBOM ecosystem interoperates at all.
Strengths: the broadest cataloger set; deterministic; PURLs and CPEs on every component; converts between formats. Limits: it inspects the artefact, so it sees what was installed, not the full dependency graph the build resolved — a jar that shades its dependencies is one component, not fifty.
Trivy
Trivy (Aqua, Apache-2.0) is primarily a scanner, but --format cyclonedx or --format spdx-json turns any scan into an SBOM, and Trivy can scan an SBOM it or Syft produced.
trivy image --format cyclonedx --output nginx.cdx.json nginx:1.27-alpineCoverage of OS packages is comparable to Syft; language coverage is slightly narrower, and the two will produce different component counts on the same image — different catalogers, different opinions about what a "package" is in a Go binary. If you already run Trivy in CI and want an SBOM as a by-product, this is the zero-effort route. Trivy Operator emits the same documents in-cluster as SbomReport resources.
docker sbom and docker scout sbom
docker sbom shipped as an experimental CLI plugin in 2022 and is a thin wrapper around Syft; it still works, and its output is Syft's. Docker's current effort is docker scout sbom, which produces an SBOM as part of the Scout service and stores it against the image in Docker Hub. Use the Scout version if you are a Scout user; otherwise run Syft directly and avoid the plugin's experimental status.
docker sbom nginx:1.27-alpine --format cyclonedx-json > nginx.cdx.json
docker scout sbom --format cyclonedx nginx:1.27-alpinecdxgen
cdxgen (OWASP CycloneDX project, Apache-2.0) is the build-time generator: point it at a source tree and it resolves the dependency graph from the package manager — npm, Maven, Gradle, pip, Go, .NET and dozens more — producing CycloneDX with the full transitive tree, scopes, and evidence. For application code it is usually more complete than an image inspection, because it sees what the build resolved rather than what survived into the artefact. It does not cover the OS layer, and it can be slow and fragile on builds that need a specific toolchain present.
cdxgen -t js -o bom.json ./checkout-serviceThe build-versus-image distinction is the real choice here. A build SBOM knows about devDependencies and build tools; an image SBOM knows about glibc and OpenSSL. For vulnerability tracking of production, the image SBOM is the one that matters — it is the artefact the kubelet runs — and the build SBOM is the one that answers licence and provenance questions. Many teams keep both and attach them to the image as attestations.
Others you will meet
- BuildKit /
docker buildxattestations —--sbom=trueat build time attaches a Syft-generated SBOM to the image manifest as an in-toto attestation. Good practice for images you build; irrelevant for the ones you pull. - Tern — layer-by-layer container inspection, Linux Foundation; slower and less maintained than Syft, but its per-layer attribution is unusual.
- Language-native plugins — CycloneDX Maven/Gradle/npm plugins,
pip-audit,cargo cyclonedx. Precise for one ecosystem, blind to everything else. - Snyk, Sysdig, Wiz, etc. — every commercial scanner exports an SBOM. Fine as a by-product; nobody chooses a platform for it.
The cluster-level SBOM is a different tool
Everything above takes an artefact you name and describes it. A cluster is not an artefact; it is a set of image digests that changes with every deploy, and roughly half of them are images nobody in your organisation built. A cluster-level SBOM tool has three jobs the generators do not:
- Discover the digests the kubelets are running — app containers, init containers, sidecars — and notice when they change.
- Generate once per digest, however many pods share it, using the pods' own pull secrets to reach private registries.
- Keep and attribute the result: one dated SBOM per image per cluster, tied to the namespaces, workloads, Helm releases and ArgoCD applications that run it, superseded rather than overwritten when the digest changes.
Trivy Operator does the first two and writes the result as a CRD with a 24-hour TTL, which is a reasonable answer for one cluster and no history. StackRadar does all three with Syft embedded in its scanner, keeps the SBOM outside the cluster, lets you download it per image, and re-matches it as advisories land. Building it yourself from Syft, a cron job and Dependency-Track is also a legitimate path — the generator is the easy part.
linux/amd64 (or the platform your nodes run) explicitly. Syft and Trivy default to the host platform, and an SBOM from an Apple Silicon laptop describes an aarch64 image your cluster never runs.Side by side
| Tool | Input | Formats | OS packages | Full dependency graph | Whole cluster |
|---|---|---|---|---|---|
| Syft | Image, directory, file | CycloneDX, SPDX, Syft JSON | Yes | Partial (what is installed) | No |
| Trivy | Image, filesystem, repo | CycloneDX, SPDX | Yes | Partial | Via Trivy Operator |
| docker sbom / scout sbom | Image | CycloneDX, SPDX (Syft under the hood) | Yes | Partial | No |
| cdxgen | Source tree, some images | CycloneDX | Limited | Yes | No |
| BuildKit attestations | Build | SPDX (Syft) | Yes | Partial | No |
| Trivy Operator | Running cluster | CycloneDX (CRD) | Yes | Partial | One cluster, 24h TTL |
| StackRadar | Running cluster(s) | CycloneDX 1.6 (Syft) | Yes | Partial | Every cluster, kept |
Format choice — CycloneDX or SPDX — is a separate question with a shorter answer; CycloneDX vs SPDX covers it.
Frequently asked questions
How do I generate an SBOM for a container image?
Run Syft against the image reference: `syft nginx:1.27 -o cyclonedx-json > sbom.json`. Use `-o spdx-json` for SPDX. Syft pulls the image with your existing registry credentials, no Docker daemon required. Trivy (`trivy image --format cyclonedx nginx:1.27`) and the `docker sbom` plugin (which wraps Syft) produce equivalent output.
What is the best SBOM generation tool?
For container images, Syft is the most widely used and the reference most other tools wrap or compare against. For application source trees — a Node or Java project with its full dependency graph — cdxgen typically produces a more complete CycloneDX document because it resolves the build rather than inspecting the artefact. For a whole Kubernetes cluster, you need a tool that discovers running images and runs one of the above per image: Trivy Operator or StackRadar.
What is the difference between syft and grype?
Syft generates an SBOM — the inventory of packages in an image or directory. Grype reads an SBOM (or an image directly) and matches the packages against vulnerability databases. They are two halves of a pipeline from the same maintainer, Anchore, and the recommended workflow is Syft once, Grype whenever the database changes.
Does docker sbom still work?
The `docker sbom` CLI plugin, released as experimental in 2022, still works and still wraps Syft, but it has not been the focus of development; Docker’s current SBOM tooling is `docker scout sbom`, which is tied to the Scout service. For a stable, scriptable SBOM from an image, run Syft directly.
Should the SBOM come from the build or from the image?
Both are useful and they answer different questions. A build-time SBOM (cdxgen, a package-manager plugin, or BuildKit attestations) knows the full dependency graph including devDependencies and build tools. An image SBOM (Syft, Trivy) knows what actually shipped, including the OS layer. For vulnerability tracking of what runs in production, the image SBOM is the one that matters, because it is the artefact the kubelet runs.