Operationalizing the Software Bill of Materials: What Dependency-Track Delivers — and Where It Fails Silently
A few months ago, a central vulnerability-management system in one cluster went down for about twenty minutes. No alert, no ticket, no phone call. Only on closer inspection did the real problem come to light — and it was far more unpleasant than the brief outage: the system had dutifully kept working for weeks, only it had been writing to the wrong database. Instead of the intended, externally backed-up PostgreSQL, everything flowed into an embedded database that nobody had backed up and nobody was watching. Nothing was broken. Everything was blind.
This very case is the reason for this article. Because it shows, in its purest form, what makes managing a Software Bill of Materials (SBOM stands for Software Bill of Materials — the machine-readable parts list of every component in an application) so treacherous in practice: such a system appears to keep running even when it has long since stopped protecting anything. And vulnerability management whose operational state nobody monitors is more dangerous than none at all — because it feigns security.
What Dependency-Track actually solves
Dependency-Track is a platform for Software Composition Analysis (SCA — the analysis of third-party and open components inside your own software), run under the roof of the OWASP Foundation. The principle is quickly explained: every build pipeline uploads one SBOM per application in the CycloneDX format. Dependency-Track breaks that parts list down into its individual components and matches each one against vulnerability databases — the National Vulnerability Database (NVD), OSV, and GitHub Advisories.
So far this sounds like an ordinary scanner. The real value, though, lies elsewhere: in the continuous re-evaluation. A classic scan à la Trivy is a snapshot taken during the build. Once the build is done, the verdict is frozen. If a critical CVE (Common Vulnerabilities and Exposures — the public catalogue of known vulnerabilities) is published tomorrow for a library that passed cleanly through the pipeline three months ago, then the classic scan learns nothing of it — until the next build.
Dependency-Track flips this logic around. The SBOM sits centrally, the vulnerability data is updated continuously, and every new CVE is computed against the entire inventory — without a single new build. Anyone who lived through Log4Shell knows why this matters: the question on the night of disclosure was not "is our code clean?" but "where all is this one library hiding?". That exact question is answered by a well-maintained SBOM inventory in minutes rather than days.
Why this is a governance topic, not a tooling topic
For the leadership level, the interesting part is not the technology but the regulatory frame it falls into. With NIS2 being transposed into national law — in Germany via the amended BSIG, whose core in §30 BSIG mandates appropriate risk-management measures for the supply chain — vulnerability management across the software supply chain is no longer optional. And §38 BSIG explicitly puts management on the hook: it must approve these measures and oversee their implementation.
Note: §30 BSIG does not use the term "SBOM" verbatim. An SBOM process is therefore not an end in itself, nor a duty spelled out in the letter of the law — it is one of the most obvious, auditable answers to the required supply-chain due diligence. Dependency-Track thus supports the implementation of NIS2 requirements; it does not "satisfy" them at the push of a button.
The second regulatory line is drawn by the Cyber Resilience Act (CRA) at the European level: from 11 December 2027 onward, an SBOM becomes mandatory for many products with digital elements. Anyone who starts operationalizing SBOMs today is therefore not building on a hunch, but toward a fixed deadline.
All well and good — continuous evaluation, a compliance angle, clear benefit. But this is exactly where the part begins that the glossy feature list conceals.
The silent failure mode: when "running" doesn't mean "protecting"
Back to the incident described at the outset. Dependency-Track ships with an embedded database called H2 (a lightweight database written in Java that runs inside the process itself) — handy for the first start, unsuitable for serious operation. For production, an external PostgreSQL is intended, connected via configuration in the deployment manifests.
The fault did not arise in the program, but in its interplay with Helm (the package manager for Kubernetes). When overlaying values, Helm replaces arrays entirely instead of merging them. So if a list of environment variables was overwritten in one place, then precisely those entries pointing to the external PostgreSQL vanished. And what does Dependency-Track do when the PostgreSQL configuration is missing? It starts anyway — and silently falls back to the embedded H2. No error, no warning, no status message. The service reports itself as healthy.
That is the crux of the matter: a watchman whose failure is so quiet that nobody notices it is a hard-to-cure disease inside the security apparatus. For weeks, uploads, evaluations, and dashboards kept running — only on a database that was not backed up and whose contents could be lost at the next restart of the pod. In this case it hit around 1,275 projects and an inventory of roughly ten million rows, which later had to be migrated laboriously. These figures come from a single real project and are no benchmark — but they do show the order of magnitude at which such silent failures do damage.
DT 5 forces the homework
There is good news for everyone who has had this trap open until now: with Dependency-Track 5.0 (generally available since 9 June 2026), the embedded H2 disappears as a production option. The new major version requires PostgreSQL — the silent fallback is thereby structurally no longer possible.
The price for this is a genuine migration effort, because DT 5 brings further breaking changes with it: separate container images for the API and the web interface, changes to API pagination and to internal data formats. Anyone still running 4.x with an embedded database today cannot get around a planned migration. I see this less as a burden than as a welcome compulsion: the version does a piece of homework that many operations would otherwise keep pushing further down the road.
Governance consequences: making the silent failures loud
The real lesson from the incident is not "PostgreSQL instead of H2". It is more fundamental: a security system must be able to raise the alarm about its own operational state. From project practice, I recommend that leadership require four questions to be answered bindingly for every SBOM system:
- Anchor the configuration in GitOps. If the desired state — including the database connection — sits in version control as the single source of truth, then a misconfiguration cannot silently arise in the live environment. Hand-made changes to the cluster stand out at the next reconciliation.
- Alert on silence. An SBOM inventory into which nothing has been uploaded for days is a warning sign — either the pipeline is jammed, or an API key is being rejected. Both must generate a ticket, not just the chance discovery.
- Monitor the data path, not just availability. A "green" health check only says that the process is alive. It says nothing about whether it is writing to the right database. Check explicitly which database the system is working against.
- Test the backup, not just its existence. A backup that has never been restored is a hope, not a plan.
This does not make failures impossible. But it makes them loud — and a loud failure is a manageable failure.
Conclusion
On balance, a clear picture remains for me: Dependency-Track is a strong, freely available OWASP project that gives the right answer to a real problem — the continuous re-evaluation of your own software supply chain instead of the snapshot in the build. For regulated organizations under NIS2/§30 BSIG and with an eye on the CRA, this is not a "nice to have" but an auditable governance building block.
But standing up a tool is not the same as operationalizing it. The silent H2 fallback is the object lesson for this: it is not the loud failure that is dangerous, but the quiet one. Operationalizing SBOMs means making the silent failure modes audible — through GitOps anchoring, alerts on rejected keys and stale uploads, monitored backups. Anyone who neglects this buys themselves a false sense of security. And a false sense of security is, in an emergency, more expensive than an honest gap, because you notice it too late.
30 minutes, no obligation — we'll help you place your status quo.
Appendix: The technical migration path (for platform engineering)
This appendix sketches the path from the embedded H2 to an external PostgreSQL under Kubernetes for everyone operationally responsible for the migration. The examples come from a real, lived Azure environment; the approach carries over analogously to other platforms.
Check the starting position. First, clarify which database the running instance is actually working against — not which one should be configured. A look at the pod's active environment variables (kubectl exec into the API container, checking the ALPINE_DATABASE_* values) shows whether the silent H2 fallback has already occurred.
Defuse the Helm array trap. The cause of the fallback is Helm's replacement behavior for arrays. All environment variables for the database connection belong in a single, complete definition — partial overlays that only want to set individual entries delete the rest. The configuration then belongs in the GitOps repository as the single source of truth.
Extract the data. The inventory sits in the H2 file on the persistent volume. For the export, the JDBC driver (the Java database interface) is extracted from the running container; via a migration script, the tables are read out and written in batches into the target PostgreSQL (ps.executeBatch() in manageable blocks rather than row by row).
Mind the order and foreign keys. The tables must be loaded in an order that respects foreign-key relationships — otherwise the import fails on orphaned references. An inventory spanning ten million rows needs patience here and enough memory in the migration pod.
Move to DT 5. Since Dependency-Track 5 requires PostgreSQL anyway, it pays to think of the migration and the version jump together. Mind the separate images for API and frontend as well as the changed API pagination, in case your own integrations depend on it.
Note: Back up the H2 state before you migrate — and test the restore into an empty PostgreSQL before you switch production over.