Pin the language runtime in the repository and enforce it in every environment. Packages resolve and behave differently on different Node or Python versions, and the runtime itself receives security fixes on its own schedule. A runtime that differs between staging and production is drift, even when every package matches.
Why the runtime counts as a dependency
A runtime is software you depend on, with its own vulnerabilities and end of support dates. It also influences which package versions get installed: many packages declare which runtimes they support, and resolvers use that information. A different runtime can therefore produce a different dependency tree from the same manifest.
What each ecosystem lets you declare
The npm package.json documentation describes an engines field, where you can state which Node versions your code works on, and which npm versions can install it. If you do not specify a version, or use an asterisk, any version will do. Poetry's documentation shows a requires-python entry in pyproject.toml that allows any Python 3 release from a stated minimum, and says Poetry will not install a Python interpreter for you.
Two lessons follow. First, these fields state a range of compatibility, not an exact version. Second, they do not install anything. They describe constraints, and something else has to provide the runtime.
| Layer | Declares compatibility | Provides the exact version |
|---|---|---|
| package.json engines | Yes | No |
| requires-python | Yes | No |
| Dockerfile base image | No | Yes |
| Version file read by a version manager | No | Helps locally and in CI |
A practical setup
- Choose one runtime version per service and write it in a version file in the repository.
- Use the same value in the base image reference, preferably pinned by digest.
- Set the compatibility field to match, so installs warn or fail on a mismatch.
- Make CI print the runtime version at the start of each job, and fail if it differs from the file.
- Add the runtime version to your drift comparison between environments.
Watch support dates
A pinned runtime still ages. Keep a calendar of end of support dates for the runtimes you use, and schedule upgrades as normal work. An environment stuck on an unsupported runtime is drift in time.
Upgrading without creating drift
Upgrade in dev and CI first, run tests, then promote the same new artifact through staging to production. Do not upgrade the runtime on production hosts in place.
Try it yourself
Click through it at your own pace, or open it full screen.
Look at how monitoring keeps checking after the first scan. Runtime and package versions both change after you ship.
What Safeguard does here
Safeguard scans dependencies and container images and monitors continuously, so findings tied to a base image or package are visible as they appear. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is the engines field enough to pin Node?
No. It declares compatibility. Pin the exact version in your base image and version file.
Does a Python virtual environment fix runtime drift?
Not by itself. It isolates packages, but it uses whichever interpreter created it.
Sources
Last reviewed: 19 October 2026.