A runtime version is usually declared in several places at once: a version file, a Dockerfile, a build config, a CI workflow and the package manifest. To inventory runtimes across many repositories, search for those files by name and read them into one table, then compare them with what actually runs.
The usual hiding places
| Place | What it tells you | Typical files |
|---|---|---|
| Version manager files | The version developers use locally | .nvmrc, .node-version, .python-version, .tool-versions |
| Container definitions | The version production likely runs | Dockerfile FROM line, base image tags |
| Language manifests | The range the project claims to support | package.json engines, composer.json, Gemfile, go.mod, pom.xml, build.gradle |
| SDK pinning files | The exact SDK a build uses | global.json for .NET |
| CI configuration | The version tests run on | Workflow files, pipeline files |
| Infrastructure code | The version of managed services | Engine version variables, serverless runtime settings |
Go is a useful example of why one file is not enough. The Go toolchain documentation says the toolchain used depends on the GOTOOLCHAIN environment setting and the go and toolchain lines in the go.mod or go.work file, and that the version can vary as you move between modules and workspaces. So even inside one language, "the version" depends on context. For the two lines explained, see go.mod go line versus toolchain line.
A search that works across repositories
You do not need special tooling to start. Clone or use your code host's search, then look for a short list of file names:
- Search for the version manager files by name in every repository.
- Search for Dockerfiles and extract each FROM line.
- Search for the language manifest and pull out the version field.
- Search workflow files for setup steps that name a runtime version.
Write each hit as a row with five columns: repository, file path, runtime, version, and source type. Then group by runtime and sort by version. The old versions will rise to the top. A spreadsheet is enough for the first pass.
Declared is not running
The files above show intent. They do not prove what runs. A Dockerfile might build from a pinned tag that someone changed in the registry. A server might have an older runtime installed by hand. Check running systems separately, for example by reading the version from the container image or host. The article on declared, built and running explains the three-way comparison, and pinning runtime and SDK versions shows how to make declared values dependable.
Common misses
- Repositories without a version file, where the answer is whatever the build image has.
- Scripts that download a runtime at build time.
- Test-only images that lag production.
- Vendored or copied runtimes inside an application bundle, such as Electron apps that ship their own.
- Serverless functions, where the runtime is a setting in the cloud console or template.
For serverless, see finding Lambda functions on a deprecated runtime.
From table to lifecycle
Once you have the table, add the lifecycle columns: status, end date, source and check date. Rows with no source are the first follow-up. Rows where declared and running disagree are the second. How to find end-of-life components in your SBOM continues from there.
Try it yourself
Click through it at your own pace, or open it full screen. Compare the inventory view with your own table of runtimes.
Where Safeguard fits
Safeguard's SBOM and dependency scanning, together with asset discovery, helps you build the component and service list that this search starts. You can run a free scan at gold.safeguard.sh/scan.
Quick check
Is the Dockerfile the most reliable source?
It is the best declared source for production, but still check the running image.
What if a repo has no version file?
Mark it Unknown and check the build image. Then add a version file so it stops being unknown.
Sources
Last reviewed: 19 October 2026.