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

Choose SPDX if… License compliance drives the request, the reader is a legal team or an OpenChain program, or the contract names the ISO standard. GitHub and Docker also hand it to you by default.
Choose CycloneDX if… Security drives the request: vulnerability triage, Vulnerability Exploitability eXchange (VEX) statements, or a buyer applying the BSI rules. It also covers software as a service (SaaS), hardware, machine learning models, and cryptography in the same format.

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

SPDX System Package Data Exchange, a Linux Foundation project. Built around licensing, with an ISO standard behind it. Its differentiator: the license vocabulary the whole industry uses, CycloneDX included.
CycloneDX An Open Worldwide Application Security Project (OWASP) Foundation project, standardized through Ecma International. Built around security and risk. Its differentiator: one format that stretches from software to SaaS, hardware, ML models, and cryptographic assets.

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.json

Here 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?

Best for a license compliance program SPDX. Legal reviewers, OpenChain, and the license list all speak it.
Best for vulnerability triage CycloneDX. VEX travels in the same file as the component list.
Best for a buyer that requires BSI TR-03183-2 CycloneDX 1.6 or later, or SPDX 3.0.1 if your tooling already writes it.
Best when the platform makes the SBOM SPDX 2.3. Take what GitHub or Docker produces unless a reader needs more.
Best when nobody named a format Both, from one Syft scan. Ship the one each reader asks for.
graph TD B{Does a rule or contract name a format?} B -->|BSI TR-03183-2| C[CycloneDX 1.6 or later] B -->|ISO/IEC 5962| D[SPDX] B -->|No| E{What is the main job?} E -->|License review| D E -->|Vulnerability triage and VEX| C E -->|Unknown| F[Generate both with Syft]

Strengths and trade-offs

SPDX

Pros
  • 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.
Cons
  • 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

Pros
  • 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.
Cons
  • 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