If you ship one Go application for several platforms, yes, generate one SBOM per build target. Build constraints such as GOOS, GOARCH, CGO_ENABLED and build tags change which modules end up in the binary, and the CycloneDX gomod tool says an SBOM should be generated for each target in the build matrix.
Why one SBOM may not fit all
Go selects files and packages at build time using constraints. A file marked for Windows is not compiled on Linux. A package behind a build tag may be left out of the default build. Since packages pull in modules, the set of modules in your binary can differ between targets.
A single SBOM from a default build on a Linux machine might therefore list a module that your Windows binary never contains, or miss one that only the Windows build pulls in.
The two subcommands
The gomod README describes two main modes and explains who each is for.
| Subcommand | What it includes | Typical user |
|---|---|---|
| app | Only modules the target application actually depends on; tests and unimported packages excluded; build constraints evaluated | Distributors who ship binaries |
| mod | Aggregate of modules required by all packages in the module; build constraints not evaluated | Maintainers tracking inventory, licenses and monitoring |
A third subcommand, bin, produces rudimentary SBOMs from binaries built with Go modules.
The README adds a practical example: a module may contain a server and a client, of which only the client is distributed. With the app mode you give users an SBOM only for what they receive.
How to set up the matrix
For app mode, the tool reads build constraints from environment variables, and these should mimic what you pass to the build. The README lists GOARCH, GOOS, CGO_ENABLED and GOFLAGS, which can carry build tags. The applicable constraints are recorded as properties of the main component, so each file says what it describes.
An example from the README:
GOARCH=arm64 GOOS=linux GOFLAGS="-tags=foo,bar" cyclonedx-gomod app -output linux-arm64.bom.xml
Steps:
- List every combination you actually release, such as linux amd64, linux arm64 and windows amd64.
- For each, set the same environment variables your release build uses.
- Run the app command with the main package path and an output name that includes the target.
- Attach each file to the binary it describes.
- Check the recorded constraint properties match the target.
When a single mod SBOM is enough
Use mod when the audience is internal, you want the whole picture, and a handful of extra modules is acceptable. It suits inventory and continuous monitoring, where over reporting is safer than missing something. It is the wrong file to send to a customer who runs one binary, since it may list modules that binary does not contain.
The cost of getting this wrong
Over reporting creates findings against code that is not in the product. Under reporting is worse, since a module compiled into one target is invisible to the file used for matching. A matrix of files costs a few CI minutes and removes both problems.
Related checks
Cgo changes things too: setting CGO_ENABLED differently can pull in different packages. Treat it as part of the target definition, not an afterthought.
Safeguard's SBOM and dependency scanning reads Go modules and SBOM files, and reachability analysis helps show which vulnerable functions your build actually calls. Try the free scan.
Quick check
Is mod or app better for customers?
The README says distributors of applications typically use app, so users get an SBOM only for the artifacts they use.
Do I need to rebuild the binary to make the SBOM?
The app command evaluates build constraints from the environment, so you set the same variables as your build. Check the tool documentation for what it needs installed.
Sources
Last reviewed: 19 October 2026.