• SBOM
  • CycloneDX
  • SPDX

CycloneDX vs SPDX: which SBOM format to use, and when it matters

Use CycloneDX for container and Kubernetes vulnerability work, SPDX for licence compliance or when a customer asks for it. Here is why, and what regulators accept.

StackRadar Team

6 min read

Both formats are international standards, both are accepted by every regulator that has expressed a view, and both can list the packages in a container image. The choice still matters, because it decides which tools can read your SBOMs and which compliance checklists they pass.

Short answer. For container images and Kubernetes vulnerability tracking, use CycloneDX 1.6. Use SPDX for licence compliance, in the Linux distribution ecosystem, or when a customer asks for it. If you have no constraint, pick CycloneDX and keep your generator able to emit both.

Where each one came from

SPDX (Software Package Data Exchange) started at the Linux Foundation in 2010 to solve licence compliance: which licences apply to which files in a distribution, precisely enough to satisfy a lawyer. That heritage shows in its depth on files, snippets, licence expressions and copyright text. SPDX 2.2.1 became ISO/IEC 5962:2021. SPDX 3.0 (April 2024, now 3.0.1) rebuilt the model around profiles, including a security profile, and is going through ISO as the next edition of 5962.

CycloneDX started in the OWASP community in 2017, explicitly for security and supply-chain use. It models components, services and dependency graphs, and carries vulnerability disclosures (VDR) and exploitability statements (VEX) natively. It is standardised as ECMA-424: version 1.6 was the first edition (June 2024), and 1.7, released in October 2025, the second.

What each one is good at modelling

SPDX 2.3 / 3.0CycloneDX 1.6 / 1.7
Package inventory with versionsYesYes
Package identifiers (PURL, CPE)Via external referencesFirst-class fields
Dependency graphRelationships (many types)dependencies[], simple and direct
File-level detail, snippetsDeepBasic
Licence expressionsDeep (its origin)Adequate
Vulnerabilities in the document (VDR)3.0 security profileNative since 1.4
Exploitability statements (VEX)Separate (OpenVEX), or 3.0Native
Services, APIsNoYes
Serialisations2.3: tag-value, JSON, YAML, RDF, spreadsheet. 3.0: JSON-LDJSON, XML, protobuf
StandardISO/IEC 5962:2021 (SPDX 2.2.1)ECMA-424 (1st ed. 1.6, 2nd ed. 1.7)

For vulnerability work, the identifier row decides it. A matcher works from a package URL (PURL): pkg:apk/alpine/nginx@1.27.5-r1?distro=alpine-3.21.3 carries the ecosystem, the distro and the exact version that an advisory's affected range is evaluated against. CycloneDX gives every component a dedicated purl field; SPDX carries it as one external reference among several. Both work, but only one was designed for it.

PACKAGE URL (PURL)pkg:apkecosystem/alpinenamespace/nginxpackage@1.27.5-r1exact version?distro=alpine-3.21.3distro qualifier

The same package in both

This is how Syft writes one Alpine package in each format.

CycloneDX 1.6
{
  "type": "library",
  "name": "nginx",
  "version": "1.27.5-r1",
  "purl": "pkg:apk/alpine/nginx@1.27.5-r1?arch=x86_64&distro=alpine-3.21.3",
  "cpe": "cpe:2.3:a:nginx:nginx:1.27.5-r1:*:*:*:*:*:*:*",
  "licenses": [{ "license": { "id": "BSD-2-Clause" } }]
}
SPDX 2.3
{
  "SPDXID": "SPDXRef-Package-apk-nginx-3c9251e68aafb845",
  "name": "nginx",
  "versionInfo": "1.27.5-r1",
  "downloadLocation": "NOASSERTION",
  "licenseConcluded": "NOASSERTION",
  "licenseDeclared": "BSD-2-Clause",
  "externalRefs": [
    { "referenceCategory": "SECURITY", "referenceType": "cpe23Type",
      "referenceLocator": "cpe:2.3:a:nginx:nginx:1.27.5-r1:*:*:*:*:*:*:*" },
    { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl",
      "referenceLocator": "pkg:apk/alpine/nginx@1.27.5-r1?arch=x86_64&distro=alpine-3.21.3" }
  ]
}

The facts are the same and only the shape differs. The CycloneDX reference has a complete, real document to read alongside the fragment above.

What the regulators actually say

  • EU Cyber Resilience Act. Annex I requires manufacturers to draw up an SBOM "in a commonly used and machine-readable format covering at the very least the top-level dependencies". No standard is named. Article 13(24) lets the Commission specify a format by implementing act, but none had been adopted when this post was updated. The SBOM obligation applies from 11 December 2027; the CRA post covers the detail.
  • United States. CISA, the NSA and international partners published the 2026 Minimum Elements for an SBOM in July 2026, replacing NTIA's 2021 list. It recommends SPDX and CycloneDX and drops the SWID tags the 2021 list also allowed.
  • Germany (BSI TR-03183-2). Version 2.1.0 (August 2025) accepts either format, but only CycloneDX 1.6 or later, or SPDX 3.0.1 or later, in JSON or XML. An SPDX 2.3 document does not meet it.

The pattern is consistent: regulators specify content and let you pick the container. That content is supplier, name, version, unique identifier, dependency relationships, author and timestamp, and CISA's 2026 list adds licence and hash. Both formats can carry all of it.

Tool support: SPDX 3 is the gap

In practice the version you can generate matters more than the format. Syft, the open-source generator that BuildKit and StackRadar both build on, writes CycloneDX up to 1.6 and SPDX 2.2 and 2.3, but not SPDX 3. Docker BuildKit's --sbom attestations are SPDX 2.3, produced by Syft. So a team that needs BSI conformance can get it from Syft today with CycloneDX, but not with SPDX.

On the consuming side, Dependency-Track and Trivy Operator are built around CycloneDX, while Grype, Trivy and OSV-Scanner read both. StackRadar is on the CycloneDX side too: it keeps a CycloneDX 1.6 SBOM for every running image, validated against the official schema, and does not ingest SPDX uploads. The SBOM tools comparison covers the generators themselves.

How to decide

ASKUSESpecified for you?a contract, a customer,a platform you upload toyesWhat they specifygenerated directly,not convertednoMain use?what the documentis forvulnerability trackingCycloneDXnative PURLs and VEXlicence complianceSPDXfile and licence depthbothEmit bothfrom the same scan
  1. Someone else specifies it. A customer contract, a procurement questionnaire, a platform you upload to. Do what they say, and generate that format directly rather than converting. Check the version too: "SPDX" under BSI TR-03183-2 means 3.0.1.
  2. Vulnerability tracking is the main use. CycloneDX. You get native PURLs, a simple dependency graph, and VEX in the same document when you need it.
  3. Licence compliance is the main use. SPDX. File-level detail and licence expressions are what it was built for.
  4. Both. Emit both from one scan. Syft accepts several outputs in one run, so the cost is one extra file per image:
bash
syft nginx:1.27 \
  -o cyclonedx-json=nginx.cdx.json \
  -o spdx-json=nginx.spdx.json

That covers one image. To see a CycloneDX SBOM kept for every image a cluster is running, open the live demo, which needs no account, or read how it works on the Kubernetes SBOM page.

Frequently asked questions

What is the difference between CycloneDX and SPDX?

Both are machine-readable SBOM standards. SPDX (Linux Foundation, ISO/IEC 5962:2021) began in 2010 as a licence-compliance format and models packages, files, snippets and their licences in depth. CycloneDX (OWASP, ECMA-424) was designed from 2017 for security: package URLs are a first-class field on every component, and vulnerability (VDR) and exploitability (VEX) data live in the same document. Both express a basic package inventory equally well.

Which SBOM format do regulators require?

None mandates one. The EU Cyber Resilience Act asks for a "commonly used and machine-readable format" and names no standard. CISA’s 2026 minimum elements, which replace the 2021 NTIA list, recommend SPDX or CycloneDX. Germany’s BSI TR-03183-2 accepts both but sets minimum versions: CycloneDX 1.6 or SPDX 3.0.1, in JSON or XML.

Is CycloneDX or SPDX more common for container images?

CycloneDX is the format most container security tooling is built around: Dependency-Track ingests CycloneDX, Trivy Operator stores its SBOM reports as CycloneDX, and StackRadar ingests CycloneDX 1.6. Syft and Trivy write both formats. SPDX is the norm in Linux distribution and Linux Foundation projects, and Docker BuildKit writes SBOM attestations as SPDX.

Can I convert between CycloneDX and SPDX?

Yes, with some loss. The CycloneDX CLI and SPDX tooling can convert existing documents, but fields that exist in one model and not the other (VEX statements, SPDX snippets, annotations) do not survive a round trip. If a customer needs a specific format, generate it from the image directly rather than converting.

Should I use CycloneDX 1.6 or 1.7?

For a container inventory, 1.6 is enough and has the widest tool support. Syft writes up to 1.6, and 1.6 is the minimum BSI TR-03183-2 accepts. CycloneDX 1.7 (October 2025, ECMA-424 2nd edition) is backward compatible and mainly adds cryptography, citation and intellectual-property metadata that vulnerability matching does not use.

What is SPDX 3.0?

SPDX 3.0, released in April 2024 (3.0.1 followed in December 2024), rebuilt the standard as a graph of elements organised into profiles (core, software, security, licensing, build, AI, dataset) and serialised as JSON-LD. Its security profile narrows the gap with CycloneDX. Generator support is still thin: Syft, for example, writes SPDX 2.2 and 2.3 only.

All posts