Start by counting how many distinct versions of each runtime you run, set a short approved list with end dates, and retire the oldest first. Sprawl is a counting problem before it is an engineering problem. You cannot shrink a list you have not written down.
Why sprawl happens
Teams pick a version when a service is created and rarely revisit it. Over a few years, one company can run several majors of the same runtime, each with a different support date. Each extra version multiplies the work: more base images, more advisories to read, more test matrices, more places a lifecycle date can be wrong.
Official policies make the cost visible. Node.js says production applications should use Active LTS or Maintenance LTS releases, and Oracle's Java roadmap lists several LTS releases at once with different end dates. If your estate spans many of those lines, you are tracking many calendars.
Step 1, count
From your inventory, produce a table per runtime: version, number of services, number of teams. Keep it plain.
| Runtime | Version | Services | Earliest end date |
|---|---|---|---|
| Example runtime | A | 40 | Stated on maintainer page |
| Example runtime | B | 12 | Stated on maintainer page |
| Example runtime | C | 3 | Stated on maintainer page |
If you cannot fill the table, the previous step is your inventory work. Where runtime versions hide in a repo helps with that.
Step 2, set the approved list
For each runtime, approve at most two lines: the primary and the next one under test. Give each an end date that is earlier than the maintainer's end date, so you retire before support ends rather than on the day. Publish the list where teams look, and name who can grant exceptions. LTS or current: which runtime version to standardize on covers how to choose.
Step 3, retire the oldest first
Order by two things: how soon the version's support ends and how exposed the services are. Take the oldest version with the fewest services. Small clusters finish quickly and prove the process. Large clusters need a staged plan.
For each cluster:
- Pick a pilot service and upgrade it.
- Write down every issue and the fix.
- Hand that note to the other teams in the cluster.
- Set a date by which the version is gone.
Use how to test a runtime upgrade for stages and exit criteria.
Step 4, stop new sprawl
Retiring old versions does little if teams keep adding new ones. Add a check at creation time: new services must use an approved version. Put the check where it is cheap, for example in a service template or a pull request gate. Make exceptions possible but visible, with an expiry.
Step 5, measure
Track three numbers monthly: distinct versions per runtime, services outside the approved list, and days until the earliest end date. If the first number goes down and the second goes down, the program works. If the third gets short while the second stays high, escalate.
A standard version does not remove vulnerabilities in libraries, so keep dependency scanning running on the approved lines too.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how versions are listed per component, which is the raw data for the counting table.
Where Safeguard fits
Safeguard's SBOM and dependency scanning gives a version-level inventory across repositories, and policies and gates can enforce an approved list at the pull request. Try the free scan at gold.safeguard.sh/scan.
Quick check
How many versions per runtime is a good target?
Two is a workable limit: the primary and the next one under test.
Should I retire the largest cluster first?
Usually start with the oldest, smallest cluster to prove the process, then plan the large ones.
Sources
Last reviewed: 19 October 2026.