Apply the org.cyclonedx.bom plugin to the root project and run the cyclonedxBom task. Gradle then writes a direct SBOM for each project and one aggregate SBOM for the build, as JSON and XML by default, built from the dependency graph Gradle actually resolved.
What is special about the Gradle plugin
The plugin's documentation says it generates CycloneDX documents from Gradle's resolved dependency graphs. It records the components and relationships Gradle selected after conflict resolution, substitution, constraints and transitive resolution. That matters: your SBOM reflects the versions you really shipped, including any constraint you added to force a patch.
Requirements
The readme lists, for the 3.x plugin line, Gradle 8.4 or newer and a build JVM of Java 8 or newer, with support for versions before Java 17 deprecated. The default schema is CycloneDX 1.6, and 1.7 can be selected with the schemaVersion setting. Check the readme for the release you install, since the page warns it documents the current branch.
Steps
- Apply the plugin in the root build file.
plugins {
id("org.cyclonedx.bom") version "3.5.1"
}
The version above is the one shown in the readme at the time of writing. Use the current release.
- Generate the aggregate SBOM.
./gradlew cyclonedxBom
- Collect the outputs. By default the aggregate lands in build/reports/cyclonedx, and the root project's direct SBOM in build/reports/cyclonedx-direct. Each subproject writes its own direct SBOM in its own build directory.
- Store the aggregate with your release artifact.
- Wire the task into CI so it runs on every release build.
Direct versus aggregate
| Type | Covers | Use it for |
|---|---|---|
| Direct SBOM | One project's own resolved dependencies | Per service records |
| Aggregate SBOM | The enabled direct SBOMs combined | A single release record for a multi project build |
The plugin supports configuration cache, parallel execution, up to date checks and the build cache, so it should not slow a modern build much.
Choose the configurations carefully
The documentation has a section on selecting configurations. By default you want what ships, which is the runtime classpath, rather than test only libraries. Decide this on purpose and write it down, because a buyer reading your SBOM will assume it describes the shipped product.
You can also exclude a project from aggregation, set component and organizational metadata, and add a CI build reference. The readme has a recipe on using SBOMs with SLSA provenance too.
Checks before you share
- Compare a handful of components to the dependencyInsight output.
- Confirm the SBOM lists the patched version if you applied a constraint. See how to force a patched transitive dependency in Gradle.
- Validate against the declared schema.
- Keep one SBOM per released version, not one that gets overwritten.
Try it yourself
Click through it at your own pace, or open it full screen. Compare the grouping of components with what an aggregate SBOM would give you.
Safeguard can ingest SBOMs, scan the dependencies behind them and apply reachability analysis, with policy gates for pull requests. Free scan: gold.safeguard.sh/scan.
Quick check
Does the plugin work with Kotlin DSL builds?
Yes. The readme shows both Kotlin DSL and Groovy DSL examples for applying it.
Why does the SBOM show a different version than my build file?
Because it records what Gradle resolved after conflict resolution and constraints, which can differ from what you declared.
Sources
Last reviewed: 19 October 2026.