A customer, an auditor, or a procurement form asks for a Software Bill of Materials (SBOM), and the first question is which format. Two standards cover most requests: SPDX and CycloneDX. Both list the components in your software, both are free, and common generators like Syft and Trivy write both, so the choice can seem arbitrary.
It stops being arbitrary once you know who reads the file. A license compliance team, a vulnerability triage pipeline, and a German government buyer want different things, and each format serves some of those readers better than the other.
Two things changed recently. SPDX 3.0 replaced the 2.x document model with a graph of linked elements (SPDX 2.3 to 3.0 differences). The SPDX project’s own migration notes call the differences “breaking changes requiring a change in logic” (SPDX 2.3 to 3.0 differences). Germany’s Federal Office for Information Security (BSI) then set a floor of CycloneDX 1.6 or SPDX 3.0.1 for new SBOMs under its technical guideline TR-03183-2 (BSI TR-03183-2 v2.1.0).
If SBOMs themselves are new to you, start with What Is SBOM? and come back.
The short answer
With no requirement from the reader, default to CycloneDX for security pipelines and generate SPDX alongside it when anyone asks. One Syft scan writes both, so producing two files costs one extra flag.
The contenders
At a glance
SPDX
- Steward. Linux Foundation; ISO/IEC 5962:2021 covers version 2.2.1.
- Current line. 3.x, though platforms still emit 2.3.
- Encodings. 2.3: JSON, YAML, RDF/XML, tag-value, spreadsheet. 3.0: JSON-LD.
- Strongest at. License data: declared versus concluded, the license list.
- Platform default. GitHub dependency graph export and Docker BuildKit attestations.
- Best for. License compliance programs and contracts naming ISO.
CycloneDX
- Steward. OWASP and Ecma Technical Committee 54 (TC54); ECMA-424 tracks the current release.
- Current line. 1.x, with no breaking major version so far.
- Encodings. JSON, XML, and Protocol Buffers.
- Strongest at. Security data: VEX, vulnerability reports, attestations.
- Platform default. None at the platform level; you add a generator such as Syft or Trivy.
- Best for. Vulnerability management and BSI-governed procurement.
How these were compared
The comparison uses the current releases as of October 2026, SPDX 3.0.1 and CycloneDX 1.7 (SPDX releases, CycloneDX releases). Sample output came from Syft scanning a small npm project that depends on lodash.
The criteria are the questions that decide the format in practice:
- Standards status. Contracts and policies sometimes name a formal standard, and the two hold different ones.
- Regulatory fit. A government rule can make the decision for you.
- Security and VEX. Security teams use SBOMs to answer “is this product affected?”
- License compliance. The original reason SBOMs existed, and still the reason legal teams ask.
- Tool support. A format your platform cannot emit or your scanner cannot read means building and maintaining a converter.
- Version stability. A format that breaks between major versions costs you a migration.
Both specifications move every year. Re-check the release pages before you quote a version number in a contract.
Head-to-head
Standards status
Winner here: tie, because SPDX holds the ISO number that older procurement language names, while CycloneDX’s Ecma standard is the one that covers the current release.
SPDX became ISO/IEC 5962:2021, which covers SPDX 2.2.1 (IEC webstore), a full major version behind the current 3.x line. CycloneDX holds ECMA-424, whose current edition defines CycloneDX 1.7 (Ecma International), so its formal standard tracks the current release.
Contract language written before CycloneDX’s Ecma standard existed had no formal standard to cite for CycloneDX, so check the wording before assuming either format qualifies. If a contract says “ISO/IEC 5962”, deliver SPDX and stop debating.
Regulatory fit
Winner here: CycloneDX, because the BSI rule rejects the SPDX 2.3 files GitHub and Docker emit by default, while Syft writes CycloneDX 1.6 and 1.7 on request (Syft output formats).
The United States baseline names both. The National Telecommunications and Information Administration’s 2021 minimum elements report lists SPDX, CycloneDX, and Software Identification (SWID) tags as the formats for SBOM automation (NTIA minimum elements).
Germany is stricter. BSI TR-03183-2 version 2.1.0 says a newly generated SBOM “MUST be in JSON- or XML-format” (BSI TR-03183-2 v2.1.0). It must also validate against CycloneDX 1.6 or higher, or SPDX 3.0.1 or higher (BSI TR-03183-2 v2.1.0).
That floor excludes SPDX 2.3 (BSI TR-03183-2 v2.1.0), which is what GitHub’s export (GitHub SBOM export) and Docker’s attestations produce by default. Docker’s scanner uses Syft’s default SPDX encoder (BuildKit Syft scanner source), and that default is 2.3 (Syft SPDX versions).
SPDX 3.0.1 still qualifies, but you must ask for it: plain -o spdx-json gives 2.3, while spdx-json@3.0 writes 3.0.1 (Syft output formats).
The floor cuts CycloneDX too: 1.5 and earlier fail it, and Syft still writes older CycloneDX versions (Syft output formats), so pin cyclonedx-json@1.6 or later.
For a BSI-governed buyer, CycloneDX at the BSI floor or above is the path with the fewest surprises.
Security and VEX
Winner here: CycloneDX, because vulnerability data is part of the format’s core, while SPDX added it only in 3.0 (CycloneDX capabilities, SPDX 3.0.1 specification).
CycloneDX lists Vulnerability Exploitability eXchange (VEX), Vulnerability Disclosure Reports (VDR), and attestations (CDXA) as first-class capabilities (CycloneDX capabilities). A VEX statement says whether a known vulnerability actually affects your product, which is what lets a triage team close a scanner alert instead of chasing it.
SPDX added VEX in 3.0, through classes in its Security profile such as VexAffectedVulnAssessmentRelationship and VexNotAffectedVulnAssessmentRelationship (SPDX 3.0.1 specification). That works, but only in 3.x documents. Since the platform defaults shown under Regulatory fit are 2.3 (Syft SPDX versions), most teams would have to opt in to get VEX.
If the SBOM feeds a vulnerability workflow, CycloneDX carries the answer in the same format as the question.
License compliance
Winner here: SPDX, because it defines the license vocabulary both formats use, and its license fields are older and deeper.
The SPDX License List is the shared identifier set. Syft’s CycloneDX output identifies lodash’s license as {"license": {"id": "MIT"}}, an SPDX identifier, and CycloneDX 1.7 added support for multiple SPDX license expressions (CycloneDX 1.7 release notes).
SPDX 2.3 gives each package separate licenseConcluded (section 7.13) and licenseDeclared (section 7.15) fields (SPDX 2.3 package information). In the sample scan with Syft, lodash came out as declared MIT and concluded NOASSERTION, which is the honest answer when nobody reviewed the code. CycloneDX caught up in 1.6, whose schema adds a license acknowledgement of declared or concluded (CycloneDX 1.6 schema), but Syft did not fill that field in the sample output.
SPDX also connects to OpenChain, the ISO standard for open source license compliance programs, which commonly uses SPDX for its bill of materials (What Is OpenChain?). For a legal reader, SPDX is the native language.
Tool support
Winner here: tie, because the main generators write both formats, and the platform defaults favor SPDX 2.x.
Syft writes both formats, in current and older versions of each (Syft output formats). Trivy generates both as well (Trivy SBOM docs).
The platforms pick for you. GitHub exports a repository’s dependency graph “using the industry standard SPDX format” (GitHub SBOM export). Docker BuildKit attaches SBOMs that “follow the SPDX standard” as JSON, generated by its Syft scanner plugin (Docker SBOM attestations).
One scan produces both files, so tool support rarely forces the choice:
syft dir:. \
-o cyclonedx-json@1.6=sbom.cdx.json \
-o spdx-json@2.3=sbom.spdx.jsonHere is the same lodash entry in each format, trimmed to the identifying fields. The CycloneDX version, from the cyclonedx-json@1.6 output (Syft output formats):
{
"type": "library",
"name": "lodash",
"version": "4.17.21",
"licenses": [{ "license": { "id": "MIT" } }],
"purl": "pkg:npm/lodash@4.17.21"
}The SPDX version, from the spdx-json@2.3 output (Syft output formats):
{
"name": "lodash",
"SPDXID": "SPDXRef-Package-npm-lodash-42055b8ceebbeac4",
"versionInfo": "4.17.21",
"downloadLocation": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "MIT",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:npm/lodash@4.17.21"
}
]
}Both carry the Package URL (purl), which is the field vulnerability databases match on, so either file can feed a scanner.
The tie breaks on the reader’s platform: if GitHub or Docker produces the SBOM, SPDX arrives for free; if a security scanner consumes it, generate CycloneDX.
Version stability
Winner here: CycloneDX, because it has moved in minor versions while SPDX’s third major version broke the document model.
CycloneDX releases have stayed on the 1.x line, with patch releases for older versions as well as the current one (CycloneDX releases).
SPDX’s 3.x line is a rewrite. The name itself changed from “Software Package Data Exchange” to “System Package Data Exchange” (SPDX 2.3 to 3.0 differences). Elements no longer need an enclosing document, and creator strings became structured agents (SPDX 2.3 to 3.0 differences). Canonical serialization is JSON that matches the JSON-LD form (SPDX serializations). Think of the 2.x model as a spreadsheet, one sheet per document with a row per package. The 3.x model is a database of records that point at each other, and a reader built for the sheet cannot follow the pointers.
SPDX 3.x files also run larger, because each license becomes a separate element plus a relationship instead of a field on the package (SPDX 3.0.1 specification).
If you depend on SPDX 2.x today, budget a migration before a rule like BSI’s reaches you.
Which should you choose?
Strengths and trade-offs
SPDX
- ISO standard, which settles contract language that names one.
- Defines the license list both formats depend on.
- Separate declared and concluded license fields for each package since 2.x.
- Default output of GitHub’s export and Docker BuildKit.
- The ISO standard covers an older 2.x release, not the current 3.x line.
- The 3.x rewrite breaks parsers written for the 2.x model.
- VEX exists only in 3.x documents.
- The German BSI guideline rejects the 2.x files most platforms emit.
CycloneDX
- VEX, vulnerability reports, and attestations are built in.
- Its Ecma standard tracks the current release line.
- Covers SaaS, hardware, ML models, and cryptography in one format.
- No platform default; you add a generator to the pipeline.
- CycloneDX 1.5 and earlier fail the BSI floor, so pin the version ([BSI TR-03183-2 v2.1.0][bsi-tr]).
- License acknowledgement fields arrived in a later release, and Syft left them empty in the sample scan.
- Less familiar to legal and open source compliance teams.
The bottom line
Pick by reader. A legal or OpenChain reader gets SPDX, a security reader or a BSI-governed buyer gets CycloneDX 1.6 or later, and a contract that names ISO/IEC 5962 gets SPDX regardless.
The cheapest move for most teams is to stop treating it as a single choice. Add one Syft step that writes both files, keep CycloneDX as the input to your vulnerability tooling, and hand out SPDX when someone asks for it. If you depend on SPDX 2.3 from GitHub or Docker, plan the 3.0.1 move now rather than when a customer’s rule forces it.
Next, read What Is OpenSSF? for the wider supply chain security picture, then Syft’s output formats to wire the scan into CI.
FAQ
Is SPDX or CycloneDX better? Neither wins outright. SPDX is stronger for license compliance and holds the ISO standard; CycloneDX is stronger for security data such as VEX and meets the BSI floor with common tools. Choose by who reads the file.
Can you convert between SPDX and CycloneDX?
Yes, with losses. syft convert reads and writes SPDX and CycloneDX, but Syft’s docs call it experimental and warn that converting between non-Syft formats loses data (Syft format conversion). Generate both formats from the original scan instead of converting one into the other.
Is SPDX only for license compliance? Not since 3.0, which added a Security profile with VEX classes. The catch is that the SPDX most platforms emit today is 2.3, which has neither.
What is the difference between SPDX 2.3 and 3.0? SPDX 3.0 replaced the single-document model with independent elements linked by relationships, renamed the standard to System Package Data Exchange, and added profiles such as Security, AI, and Dataset (SPDX 3.0.1 specification). Parsers written for 2.3 need changes to read 3.0.
Which SBOM format does GitHub export? SPDX, as JSON, from the repository’s dependency graph. If a reader needs CycloneDX, run a generator such as Syft or Trivy in CI instead.
References
- SPDX specifications, for the list of SPDX versions.
- SPDX spec releases on GitHub, for release history.
- SPDX 3.0.1 specification and SPDX serializations, for profiles, VEX classes, and canonical JSON.
- SPDX 2.3 conformance and SPDX 2.3 package information, for the 2.3 encodings and license fields.
- SPDX 2.3 to 3.0 differences, for the breaking changes and the name change.
- ISO/IEC 5962:2021 on the IEC webstore and the Linux Foundation announcement, for SPDX’s ISO status.
- CycloneDX specification overview, for encodings and governance.
- CycloneDX releases on GitHub and the 1.7 release notes, for versions and license expression changes.
- CycloneDX capabilities, for VEX, VDR, attestations, and the BOM types.
- CycloneDX 1.6 JSON schema, for license acknowledgement.
- ECMA-424, for CycloneDX’s Ecma standard editions.
- NTIA minimum elements for an SBOM, for the United States format list.
- BSI TR-03183-2 v2.1.0, for the German minimum versions and encodings.
- Syft output formats, Syft format conversion, Syft SPDX versions, and Syft releases, for generator support, conversion limits, the default SPDX version, and release history.
- BuildKit Syft scanner source, for the SPDX encoder Docker’s attestations use.
- Trivy SBOM docs, GitHub SBOM export, and Docker SBOM attestations, for platform defaults.
Related Content
- What Is SBOM? explains what goes in a bill of materials and why it exists.
- What Is OpenChain? covers the license compliance standard that leans on SPDX.
- What Is OpenSSF? maps the open source security foundation behind much supply chain tooling.
- Fundamentals of Software Security puts SBOMs in the context of the rest of a security program.

Comments #