Generating CycloneDX SBOMs From Nix Closures With Nix2sbom
A tool for converting Nix's complete dependency graphs into machine-readable SBOMs.

Nix builds software from a dependency graph that is fully known before a single line of code compiles. That property, more than any feature bolted onto the build system, is what makes a tool like nix2sbom worth writing about, because it converts a Nix closure, the complete set of store paths a build depends on, into a CycloneDX document that auditors, scanners, and compliance officers can actually use. The rest of this piece walks through how that conversion works, where it runs, and where it still falls short.
Start with the derivation, because everything else in Nix depends on it. A derivation is the instruction set for a reproducible build, naming a builder, a target system, and a set of arguments, and Nix stores it as a .drv file before anything gets built. The closure is what surrounds that derivation: the transitive set of every store path Nix needs to have on hand in order to reproduce the build. That includes obvious things like compilers and standard libraries, but it also includes bootstrap infrastructure most developers never think about, the tools used to build the tools.
This closure splits into two distinct notions. The buildtime closure is computed from the.drv file before the target is built. The runtime closure, by contrast, reflects the actual build outputs and requires the target to be built first before that closure exists. A simple hello-world C program shows how much machinery a trivial task depends on, since pulling the closure reveals gcc, the full standard environment (stdenv), glibc, and bash, all present as transitive dependencies of a program that prints one line of text.
The content-addressed store is what makes any of this enumerable, or trustworthy. Every input to a Nix build gets hashed, and identical inputs always produce the same store path. That's what makes the dependency graph both fully enumerable and tamper-evident, since any change to an input changes the hash, which changes the store path, which breaks any claim of correspondence to what's actually running. It's what makes the dependency graph both fully enumerable and tamper-evident, since any change to an input changes the hash, which changes the store path, which breaks any claim of correspondence to a known-good build.
This is the specific problem nix2sbom is built to solve. A static SBOM generated outside of this model, by scanning a compiled binary or reading a package manifest after the fact, can miss transitive dependencies, miss unexpected substitutions, or miss components pulled in from a compromised registry somewhere upstream. Nix's closure model doesn't have that blind spot, because every input is explicit at build time, not inferred afterward. That's what makes a real provenance claim possible: knowing not just what's in the final binary, but knowing the provenance of every source that touched it, including the toolchains and the compilers that built it. A no-third-party-interference claim requires the graph. It cannot be made from the output binary alone.
That completeness is the opportunity. It is also the complexity nix2sbom has to manage, because a fully enumerated closure is not automatically a usable SBOM.
How nix2sbom converts a Nix closure into a CycloneDX document
nix2sbom is a Rust command-line tool that extracts CycloneDX and SPDX SBOMs from Nix derivations. It shows 19 GitHub stars as of the sbom-generator topic listing, a modest number to keep in mind when weighing it against the other tools covered later.
The core of the tool is a structure called the PackageGraph, which represents the relationships between Nix store paths. Building an SBOM from a closure isn't a matter of listing paths; it requires a transform phase that does three things at once. It identifies which derivations in the closure are "source" derivations, separating actual software components from the build machinery that produced them. It detects patches applied during the build, so the SBOM reflects not just what went in but what was modified along the way. And it maps Nix-specific attributes onto Package URLs (PURLs), the standard identifier format that lets downstream tools talk about the same component using the same name.
Format output is handled through a Format enum, a strategy pattern that keeps the graph logic decoupled from the specifics of CycloneDX versus SPDX serialization. Same graph, different writer. That's a sensible design choice, since it means adding SPDX support (or a future format) doesn't require touching the code that actually builds the dependency model.
Two of the automatic discoveries go beyond what most SBOM tools attempt. nix2sbom resolves archive URLs back to upstream Git URLs, so the document doesn't just say "this is version 1.2.3 of some library," it points to where the source actually lives. And it tracks patches applied during the build: a vulnerability scanner matching against a public CVE database needs to know if a known flaw was patched out locally, something a bare version string can't communicate.
As of v2.4.5, nix2sbom supports CycloneDX 1.4 as its stable output and SPDX 2.3 as an experimental one, with both JSON and YAML serialization available. It also supports generating a system-wide SBOM for a NixOS configuration.
The PURL mapping deserves a beat of its own. Translating a Nix store path (something like /nix/store/abc123-openssl-3.0.2) into a PURL that a vulnerability scanner recognizes is not a trivial string operation, because Nix's naming conventions don't map cleanly onto the package registries most PURL schemes assume. A PURL alone tells a scanner what package and version it's looking at. The Git URL nix2sbom attaches on top of that tells an auditor where the source actually came from, which is the difference between version matching and actual supply chain verification.
Running nix2sbom in a GitHub Actions CI pipeline
The nix2sbom repository publishes a GitHub Actions workflow, current as of v2.4.5, and it's short enough to describe in four steps. First, check out the repository with actions/checkout@v4. Second, install Nix, using the DeterminateSystems/nix-installer-action pinned to a specific commit hash (da36cb69b1c3247ad7a1f931ebfd954a1105ef14, corresponding to v14). Third, install the nix2sbom binary itself via EricCrosson/install-github-release-binary, fetching louib/nix2sbom/nix2sbom@v2.4.5. Fourth, run the tool: nix2sbom .# -f spdx > sbom.spdx.json.
Note the flag in that last step targets SPDX, not CycloneDX; teams wanting CycloneDX output use a different format flag in that same command.
Outside of CI, there are two other install paths. nix profile install github:louib/nix2sbom works if flakes and the nix-command experimental features are already enabled, which most modern Nix setups have turned on by default at this point. Or build from source with cargo install --path . if a binary release isn't available for the target platform.
That last option isn't hypothetical. There's an open issue (#45, filed August 2024) noting that no ARM binary is published yet, which means teams running CI on ARM-based runners, increasingly common given the cost and power advantages of ARM in cloud infrastructure, have to either build from source or lean on Nix itself to install the tool rather than pulling a prebuilt binary.
One more practical note on usage: the .# in the example command points at the current flake in the working directory. Teams don't have to point nix2sbom at their own repository. Pointing it at a specific flake reference or store path lets a team generate an SBOM for a dependency they're pulling in, not just for their own code, which is often the more useful audit target.
The workflow itself is lightweight enough to bolt onto an existing Nix-based CI setup without much friction. What that generated document actually covers, and where it falls short, matters more.
What nix2sbom does not yet cover, and open issues on the roadmap
CycloneDX has moved past 1.4, and nix2sbom hasn't caught up yet. Support for 1.6 is tracked as an open issue (#41, filed August 2024), and 1.5 support sits in a separate open issue (#5) marked "help wanted," meaning the maintainers are looking for outside contribution to close it. That version gap is becoming less academic by the month: CISA's 2026 minimum elements, effective July 29, 2026, add requirements around component hashes, licenses, and SBOM document metadata, and staying on an older CycloneDX schema version makes it harder to represent those fields cleanly.
Identifier coverage has gaps too. CPE, the Common Platform Enumeration format that many vulnerability scanners and compliance frameworks expect alongside or instead of a PURL, isn't implemented yet (issue #18). PURL detection itself is flagged for improvement (issue #20), meaning some components may get incomplete or inaccurate PURLs, which quietly degrades scanner accuracy downstream even when the rest of the document is solid. Properties specific to that packaging ecosystem aren't fully populated on components yet either.
There are structural gaps as well. CycloneDX supports sub-components, a way of representing nested dependency relationships within a single component, and nix2sbom doesn't support that yet (issue #14), which limits how precisely nested build relationships can be expressed. Output isn't sorted (issue #8), a small thing that nonetheless makes diffing two SBOM runs noisier than it needs to be for teams tracking drift over time. Protobuf serialization isn't supported (issue #44).
Distribution is its own gap. nix2sbom isn't published to nixpkgs yet (issue #28, filed November 2023), so installing it means going through a Nix flake reference or Cargo rather than pulling it from the standard package channel most Nix users reach for first.
There's a draft pull request (#50, opened September 2024) that adds a vulnerability-scanning feature, which suggests the roadmap is heading toward integrated CVE correlation somewhere down the line. It hasn't merged, so it's not something to plan around yet.
And it bears repeating: SPDX output is explicitly marked experimental. Teams submitting SBOMs for compliance purposes should not treat that output as stable without validating it against whatever standard they're being held to.
None of this means the tool isn't usable today, but the gaps are specific enough that they should shape a rollout decision. A team evaluating nix2sbom for a compliance pipeline should check upfront whether CPE absence blocks their scanner integration, and whether CycloneDX 1.4 output satisfies the specific standard they're targeting.
How nix2sbom compares to the other active Nix SBOM generators
nix2sbom isn't the only tool doing this work, and it isn't the most widely adopted one. Four Nix-specific SBOM generators are active right now, all open source, and GitHub star counts offer the best rough proxy available for how much traction each has picked up.
bombon, maintained by nikstur, leads the field with 140 stars according to best-of-nix, the most-starred Nix-specific SBOM tool among those confirmed here. It generates CycloneDX SBOMs with component data, license data, SPDX identifiers, and PURLs, and it was recently updated.
Genealogos, from Tweag, is at 55 stars and takes a different architectural approach: it consumes output from a separate tool called nixtract and produces CycloneDX in three schema versions, 1.3, 1.4, and 1.5, in both JSON and XML. Genealogos also runs a public website with pre-generated SBOMs for every package in nixpkgs, paired with a viewer built for humans rather than machines. It's funded through NLNet's NGI0-entrust program. nixtract restarts Nix for every single SBOM component it processes, so evaluating a complex derivation can take a while.
nixtract itself, the extraction tool Genealogos builds on, has 110 stars on its own and works by pulling the derivation graph out of a Nix flake. The CycloneDX Tool Center lists Genealogos, built on nixtract, as a standalone option for generating SBOMs without needing to build the derivations first, which is a meaningful distinction for teams that want SBOM data without the cost of a full build.
sbomnix, maintained by tiiuae as part of the Ghaf Framework, has 42 stars, and it's the broadest suite of the four by a wide margin. The core sbomnix tool generates SBOMs from either a flake reference or a store path, and when the target is a flake reference, it enriches the output with nixpkgs metadata like descriptions, license text, maintainer names, and homepage links. Around that core sit several companion tools: nixgraph for dependency graph queries and visualization, vulnxscan as a vulnerability scanner built directly on the SBOM output, and a provenance subcommand that generates SLSA v1.0 compliant attestation files for a flake or derivation. There's also a small cluster of package-currency tools, repology_cli, repology_cve, and nix_outdated, for tracking how stale a given dependency set has gotten. A separate but related project, tiiuae/flakevuln, packages a reusable GitHub Action for running daily vulnerability scans and depends on sbomnix.
nix2sbom, at 17 stars, is the smallest of the four by adoption signal, but patch detection and Git URL discovery are built directly into the PackageGraph transform step, which is a narrower but genuinely useful focus for teams that specifically need patch-level provenance in their CycloneDX output. Its footprint is lighter than sbomnix's full suite, which is either an advantage or a limitation depending on how much tooling a team wants to adopt at once.
sbomnix's provenance and vulnerability-scanning integration make it the fullest supply-chain-security platform of the four. Genealogos offers the best browsing experience for exploring nixpkgs SBOM data. bombon has the strongest adoption signal by star count. nix2sbom's patch tracking is a specific, narrow strength. None of these tools are mutually exclusive, and nothing prevents a team from composing more than one of them in a single pipeline, using bombon or Genealogos for broad CycloneDX coverage while relying on nix2sbom specifically where patch provenance matters most.
The CycloneDX Tool Center lists Genealogos and nixtract as compliant with BSI TR-03183 and a federal directive establishing SBOM requirements, which functions as a compliance-readiness signal for teams already facing those specific mandates.
The compliance landscape that makes Nix SBOM generation worth doing now
The regulatory backdrop here has moved fast, and the direction of travel matters more than any single memo, so here is the sequence laid out in order.
A federal directive establishing SBOM requirements, signed in May 2021, is the originating requirement: it mandated SBOMs for federal vendors, and that core mandate is still intact. A later federal directive, from January 2025, added enhanced attestation requirements and a centralized validation process run through a federal cybersecurity agency. Then a subsequent federal directive, signed June 6, 2025, rescinded key portions of the earlier one, pulling back the enhanced attestation mandates, the requirement that vendor SBOMs be treated as a high-level compliance artifact, and the centralized validation process run through that agency. What survived that rollback are the underlying technical requirements in NIST SP 800-53, NIST SP 800-218, and FedRAMP.
The rollback continued into 2026. A federal budget office memorandum, dated January 23, 2026, rescinded the mandatory attestation memos M-22-18 and M-23-16, which means individual federal agencies now build their own risk-based assurance processes rather than following a single centralized standard.
None of that removes the practical need for SBOMs, and one date on the calendar makes that especially clear: July 29, 2026, when CISA's 2026 minimum elements baseline formally replaces the older NTIA 2021 baseline. That new baseline adds requirements for component hashes, license data, and SBOM document metadata, which is exactly the kind of granular detail a Nix closure is well positioned to supply, given that hash-based identity is already how the content-addressed store works.
Outside that country, the pressure is if anything more direct. The EU's Cyber Resilience Act mandates SBOMs as a condition of market access, and Germany's BSI TR-03183-2 spells out the detailed technical implementation guidance vendors are expected to follow. South Korea announced its own SBOM requirement in October 2025, covering IT systems and products in the public sector, set to take effect in 2027.
A common expectation runs through all of these regimes: agencies and regulators want machine-readable SBOMs, in SPDX or CycloneDX, for critical software components, and they want those SBOMs kept current as software updates roll out. That "kept current" requirement is exactly where Nix's model earns its keep, because an SBOM generated from a derivation regenerates cleanly whenever the derivation changes. There's no separate bookkeeping process to keep in sync.
For a team actually running nix2sbom, CycloneDX 1.4 output satisfies a meaningful share of current requirements, but the CISA 2026 additions and the still-open CycloneDX 1.6 upgrade (issue #41) are a live risk for anyone specifically targeting federal or EU compliance in the next year or two. Regulatory churn at the federal level hasn't removed the requirement to produce a defensible SBOM. It has shifted the judgment call down to the agency level, which if anything raises the bar for how auditable and precise that SBOM needs to be.
Using Nix SBOM output for vulnerability detection and supply chain policy enforcement
An SBOM only pays for itself once something consumes it, and the primary consumer here is vulnerability correlation. Tools such as Safeguard, as of 2024, ingest Nix-generated CycloneDX SBOMs and check the components listed against OSV, GHSA, and the NVD, scanning the entire native closure rather than stopping at whatever package sits at the top of the dependency tree. That distinction matters enormously in practice, since the vulnerability in question is far more often three or four layers down in a transitive dependency than in the package a team actually chose to install.
That same SBOM data can back policy enforcement. A CI pipeline can block a flake update if it introduces a transitive dependency with a known vulnerability, or if it changes a narHash value without a corresponding signed commit backing that change. That's enforcement happening at the level of the closure itself.
sbomnix's vulnxscan tool is a working example of what a composed pipeline looks like: generate the SBOM, run the vulnerability scan against it, surface the results, all inside the same Nix-native toolchain rather than shipping data out to a separate system. The flakevuln GitHub Action takes that same pipeline and automates it on a daily schedule, so the scan runs continuously rather than only at release time.
The hash-level provenance the Nix store provides by default is what makes all of it possible. Every store path carries a hash derived from its inputs, and that hash-based identity is what lets a vulnerability scan, or a policy gate, or a compliance audit, all point at the exact same artifact and agree on what it is. That's a much stronger foundation than matching on a package name and a version string, which is the best most SBOM tooling outside the Nix ecosystem can offer.
Sources
- Build software better, together
- GitHub - louib/nix2sbom: nix2sbom extracts the CycloneDX and SPDX SBOM (Software Bill of Materials) from a Nix derivation
- Issues · louib/nix2sbom
- Pull requests · louib/nix2sbom
- CycloneDX Tool Center | CycloneDX
- GitHub - tiiuae/sbomnix: SBOM, provenance, dependency graph, and vulnerability tools for Nix.
- GitHub - nikstur/bombon: Nix CycloneDX Software Bills of Materials (SBOMs)
- github.com


