NODE VERSION DRIFT CHECK · RUNS IN YOUR BROWSER

Do your package.json, .nvmrc, Dockerfile and CI agree on which Node.js to run?

Paste the files that declare a Node.js version and see where they drift apart: a pinned version that cannot satisfy engines.node, pins that disagree on the major version across files, a CI matrix or pin on an end-of-life Node.js, and an engines.node range with no upper bound and nothing pinned.

Nothing is uploaded. Everything runs in this browser against the text you paste in. End-of-life dates are a static table copied from nodejs.org on 2026-10-11 and shown as of the date you pick.

Check a repo's Node.js versions

Leave a box empty if the repo has no such file. The boxes start with a worked example that has real drift in it.

Prefer a CLI, or want this in CI before a merge?

Same logic, zero dependencies, one command per repo:

node cli.mjs                       # scan the current directory
node cli.mjs /path/to/repo         # scan a specific checkout
node cli.mjs --format json
node cli.mjs --fail-on-risk        # exit 2 if any risk finding exists
node cli.mjs --today 2026-10-11    # judge end-of-life as of a fixed date

Get cli.mjs and engine.mjs — Node.js 18+, nothing to install, no network call ever. --fail-on-risk makes it a one-line CI gate.

What's checked

CheckHow
Pin violates engines.nodeA version from .nvmrc, .node-version, .tool-versions, a Dockerfile FROM node: tag, Volta's volta.node or a setup-node step that no matching version could satisfy against engines.node
Majors disagreeNon-matrix pins in different files that name different Node.js majors (a CI matrix is meant to test several, so it is exempt)
End-of-life Node.jsAny pin or matrix entry whose major is past its end-of-life date in the dated table, plus a notice when one is within 90 days
Unbounded enginesengines.node like >=18 with no upper bound while no file pins a Node.js version at all

Aliases such as lts/*, latest and node change over time, so they are listed as notices and never guessed. A ${{ matrix.x }} reference is resolved from the workflow's own matrix. Also read: why Node.js versions drift, and how this compares to eyeballing the files or npm's engine-strict.

Common questions

Why check Node.js versions across files at all?

A repo usually states its Node.js version in four or five places that nobody keeps in step: engines.node in package.json, an .nvmrc, the Dockerfile base image, and the GitHub Actions setup-node step. When they drift, local development, CI and production quietly run different majors, so a bug appears in one and not the others, or a dependency that needs a newer Node.js is installed on an older one.

How does it decide whether a pin satisfies engines.node?

It has its own small semver range parser (caret, tilde, comparators, hyphen ranges, x-ranges, spaces as AND, double pipe as OR). A pin such as 20 is treated as any 20.x version, so it is only flagged when no 20.x version could possibly satisfy the range; an exact pin such as 20.4.0 is checked exactly.

Where do the end-of-life dates come from, and can they go stale?

They come from a small table copied from the Node.js release schedule at nodejs.org/en/about/previous-releases, labelled with the date it was written. Even majors end on April 30 of their third year and odd majors are never long-term support. The table is a snapshot, so a major newer than the table is reported as unknown rather than supported, and you should confirm anything important on nodejs.org.

Is anything uploaded?

No. Everything is computed in your browser from the text you paste in, and nothing you type is sent anywhere. The CLI makes no network call either; it only reads files already on your own disk.