Running locally is useful for trying the tool out, debugging a pipeline that behaves unexpectedly, generating a one-off SBOM, and running the setup wizard - which is interactive and deliberately refuses to run in CI.
For actual SBOM generation in a pipeline, use your CI platform.
Docker
Closest to what CI does, which makes it the best choice for reproducing a pipeline problem:
docker run --rm \
-v "$(pwd):/github/workspace" \
-w /github/workspace \
-e LOCK_FILE=requirements.txt \
-e OUTPUT_FILE=sbom.cdx.json \
-e ENRICH=true \
-e UPLOAD=false \
ghcr.io/sbomify/sbomify-actionNote that the container runs as root, so generated files will be root-owned on the host. Add --user "$(id -u):$(id -g)" if that is inconvenient.
uvx
If you have uv, you can run the CLI without installing anything permanently:
uvx sbomify-action --lock-file requirements.txt --enrich --no-upload -o sbom.cdx.jsonuvx downloads the package into a temporary environment, runs it, and leaves nothing behind. Ideal for a one-off.
The setup wizard works the same way:
uvx sbomify-action wizardThe license database generator is a separate entry point in the same package:
uvx --from sbomify-action sbomify-license-db --distro alpine --version 3.20 -o alpine-3.20.json.gzpipx
pipx run is the equivalent if you use pipx rather than uv:
pipx run sbomify-action --lock-file requirements.txt --enrich --no-upload -o sbom.cdx.jsonpipx run sbomify-action wizardBoth uvx and pipx run keep the tool out of your global Python environment, which is what you want for something you invoke occasionally.
The generators come down on first use
You do not need to install Syft or cdxgen yourself. The tool downloads the generators it needs on first use, verified against a pinned digest and cached under ~/.cache/sbomify/runtimes (or wherever SBOMIFY_TOOL_CACHE points).
That means uvx sbomify-action --lock-file Cargo.lock ... gets you cargo-cyclonedx, not a Syft fallback, without any setup. The first run for a given ecosystem pays a download; later runs reuse the cache.
If you would rather use only what is already on your machine, opt out:
SBOMIFY_FETCH_RUNTIMES=0 uvx sbomify-action --lock-file requirements.txt --enrich --no-upload -o sbom.cdx.jsonBe aware of what that trades away. Opting out does not make the tool fail when a native generator is missing - it falls back to whatever is installed, which is usually a worse answer, and the SBOM does not say so. If you set this flag, install the generators you care about:
| Tool | Install | Covers |
|---|---|---|
cyclonedx-py | Bundled as a dependency | Python |
| Syft | brew install syft or the install guide | Most ecosystems, container images, SPDX output |
| cdxgen | npm install -g @cyclonedx/cdxgen | JavaScript, Ruby, PHP, .NET and more |
cargo-cyclonedx | cargo install cargo-cyclonedx | Rust |
crane and cosign | brew install crane cosign | Chainguard image detection |
If a required tool is missing and fetching is disabled, the CLI tells you which one and how to install it.
Environment variables work too
Every CLI flag has an environment variable equivalent, which is convenient in scripts:
export LOCK_FILE=requirements.txt
export OUTPUT_FILE=sbom.cdx.json
export ENRICH=true
export UPLOAD=false
uvx sbomify-actionFlags take precedence over environment variables. See the configuration reference.
Uploading from your machine
export SBOMIFY_TOKEN="your-token"
uvx sbomify-action \
--component-id your-component-id \
--lock-file requirements.txt \
--augment --enrichOIDC trusted publishing is not available locally - it depends on a CI-issued identity token - so use an API token.
Be deliberate about uploading from a laptop. An SBOM generated locally reflects your machine rather than a clean build, and it cannot be attested. It is fine for experimenting; for anything you intend to distribute, generate it in CI where it can be signed at origin.
VCS information
Nothing is auto-detected locally. Set the values in sbomify.json if you need them:
{
"vcs_url": "https://github.com/my-org/my-repo",
"vcs_commit_sha": "abc123def456",
"vcs_ref": "main"
}See augmentation.
Debugging a pipeline
When CI produces something you did not expect, reproduce it locally with the same image and the same variables:
docker run --rm \
-v "$(pwd):/github/workspace" \
-w /github/workspace \
-e LOCK_FILE=requirements.txt \
-e OUTPUT_FILE=sbom.cdx.json \
-e ENRICH=true \
-e UPLOAD=false \
-e VERBOSE=true \
ghcr.io/sbomify/sbomify-actionVERBOSE=true shows which generator ran, which enrichment sources answered, and where time went. Check audit_trail.txt afterwards for the full list of changes.
Two differences from CI worth keeping in mind: VCS auto-detection will not fire locally, and enrichment coverage may differ if CI is hitting GitHub API rate limits that your machine is not.
Next steps
- Quick start - the setup wizard
- Configuration reference - every option
- Your CI platform - moving this into a pipeline