The fastest answer to “how do I check the Node version” is node --version. That is the answer every Stack Overflow thread hands you. It is also the answer that is right about half the time and dangerously wrong the other half, because the Node your terminal sees is not the Node your app runs on, the Node CI tests against, or the Node Docker picks from the base image. The version that matters is the one in the runtime environment where the code actually executes, and getting there means checking four places, not one.
Table of contents
- Table of contents
- The direct answer
- The four places the Node version lives
- Why
node --versionlies on macOS - Why
node --versionlies in CI - Why
node --versionlies in Docker - The right way to pin a Node version for a project
- The five-minute audit that catches version drift
- FAQ
- FAQ
The honest short version: node --version tells you what node resolves to in your current PATH. npm exec node -- --version tells you what version a tool like npx will pick. process.version in code tells you what version the running process actually is. The .nvmrc or engines.node in your project tells you what version the project expects. None of these agree by default. The fix is to pin a version in .nvmrc and check all four.
Table of contents
- The direct answer
- The four places the Node version lives
- Why
node --versionlies on macOS - Why
node --versionlies in CI - Why
node --versionlies in Docker - The right way to pin a Node version for a project
- The five-minute audit that catches version drift
- FAQ
The direct answer
Three commands, in order of how often they solve the problem:
node --version
# v20.11.1
That is the headline. Fast, simple, almost always wrong about what matters.
node -p "process.versions"
# { node: '20.11.1', v8: '12.4', uv: '1.47.0', ... }
That is the version the running process actually is, including V8, libuv, and OpenSSL. Use this when you need the truth about what your code is running on.
cat .nvmrc
# 20.11.1
That is what the project expects. If this disagrees with node --version, the project is broken.
The fourth place is the runtime environment — the production server, the Docker image, the Lambda function — and that one does not have a single command. You check it the same way you would check any other runtime: log it, expose it as a health endpoint, or print it at startup.
The four places the Node version lives
A Node.js deployment has at least four versions in play at any moment. They are usually different.
| Place | What it tells you | Command |
|---|---|---|
Shell node --version | What node resolves to in your PATH right now | node --version |
Project package.json | What the project pinned in engines.node | cat package.json | jq '.engines' |
Project .nvmrc | What nvm use will switch to in this directory | cat .nvmrc |
Running process process.version | What the actual Node binary is that is executing your code | node -p "process.version" |
If your team is disciplined, three of these agree. If your team is normal, they all disagree and the CI runs on a different Node than production.
Why node --version lies on macOS
The Node your terminal sees on macOS is whichever of these wins the PATH race:
- The system Node at
/usr/bin/node— usually old, usually EOL. - Homebrew’s Node at
/opt/homebrew/bin/node— current, but not version-pinned unless you usedbrew install node@20. - nvm’s Node at
~/.nvm/versions/node/<version>/bin/node— only in your shell afternvm use. - Volta’s Node at
~/.volta/tools/image/node/<version>/bin/node— only in your shell after Volta initializes. - n’s Node at
/usr/local/n/versions/node/<version>/bin/node— only in your shell aftern use.
Each of these is a real Node. None of them is “the Node your app uses.” The one your app uses is the one it ships with, the one your CI installs, or the one your Dockerfile pins. The terminal version is decorative.
A practical test: open a fresh shell, do not run nvm use, run node --version. If the result surprises you, your project is one missing nvm use away from running on the wrong Node.
Why node --version lies in CI
CI runners ship with a default Node installed. That default is almost always a recent LTS, but it changes as the runner image updates. If your project does not pin a Node version explicitly, your CI is running whatever the runner happens to have today. The test that passed yesterday can fail tomorrow because the runner image updated.
The fix is in two places:
- In the CI config. GitHub Actions:
actions/setup-node@v4withnode-version-file: '.nvmrc'. GitLab CI:image: node:20.11.1or use thenvminstall. CircleCI:docker: cimg/node:20.11. - In the project. A
.nvmrcfile with the exact version, anengines.nodefield inpackage.json, an.node-versionfile for tools that look there.
If both are in place, CI uses the same Node your team uses locally. If one is missing, CI silently drifts.
Why node --version lies in Docker
Dockerfiles that start with FROM node pick whatever :latest or :20 happens to point at on Docker Hub. node:20 resolves to a moving target — it is “Node 20.x at the time of build.” Your build from last month is not the same as today’s build, even with the same Dockerfile.
The fix is a version tag:
FROM node:20.11.1-bookworm-slim
The full version tag, not just the major. The slim variant, not the default. The Debian base, not Alpine (Alpine has musl-libc, which breaks some native modules). That is the Dockerfile that builds the same way in 2027 as it does today.
If you are deploying a Dockerfile that started life as FROM node:20, fix it now. The Node 20.11.1 image and the Node 20.18.0 image differ in ways that matter for some packages, particularly anything that touches OpenSSL or native bindings.
The right way to pin a Node version for a project
The minimum useful setup:
-
.nvmrcat the repo root, containing exactly one line:20.11.1That is what
nvm usereads, whatfnm usereads, what Volta reads, whatactions/setup-nodereads. -
engines.nodeinpackage.json:"engines": { "node": ">=20.11.1 <21" }npm will warn if a developer is on the wrong version. Some teams add
"engines": { "node": "20.11.1" }for a hard pin and let CI reject anything else. -
.node-versionif your tooling looks there. Some editors and CI runners prefer it. -
Dockerfile tag matching the same version.
If all four say 20.11.1, your team and your CI and your production are on the same Node. That is the goal.
The five-minute audit that catches version drift
The audit you can run today on any Node project:
echo "Terminal:"; node --version
echo ".nvmrc:"; cat .nvmrc 2>/dev/null || echo "(missing)"
echo "package.json engines.node:"; jq -r '.engines.node // "(missing)"' package.json
echo "Dockerfile FROM node:"; grep -E '^FROM node' Dockerfile 2>/dev/null || echo "(no Dockerfile)"
echo "CI setup-node version:"; grep -E 'node-version' .github/workflows/*.yml 2>/dev/null || echo "(no GitHub Actions)"
echo "Running process:"; node -p "process.version"
If those six lines do not all agree, you have version drift. Fix the .nvmrc first, then update engines.node, then rebuild the Dockerfile with the pinned tag, then update the CI action. Five minutes of work for a class of bugs you stop having.
If you are running this audit on a project you are about to deploy to a platform like RunxBuild’s backend services, the same engines.node field in package.json is what tells the platform which Node to pick. The build log will print the resolved Node version, and the deploy log will print it again, so version drift between local and deploy stops being a silent failure. For the deploy cost of running that node process at the version you just pinned, the RunxBuild hosting calculator will give you a per-month number against your current provider before you commit.
FAQ
What is the difference between node --version and node -v?
Nothing. Both print the version and exit. -v is shorter, --version is longer. Use either.
How do I check the version of an older Node I have installed?
nvm ls lists every installed version. fnm list does the same. Pick the one you want with nvm use 18.20.4 and check again.
What is the LTS version right now?
Node releases a new major every six months and an LTS every October. As of mid-2026, Node 22 is the active LTS, Node 20 is in maintenance LTS, and Node 18 has reached end-of-life. The node --version of any modern install should be 20.x or 22.x.
Why does my .nvmrc say one thing and node --version say another?
Because nvm use is opt-in. nvm does not automatically switch versions when you cd into a directory unless you have shell integration enabled. Add [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" to your shell rc, or install a shell plugin that auto-switches.
Can I check the Node version from inside a running app?
Yes. process.version returns the Node version string. process.versions returns an object with node, v8, uv, openssl, and others. Use process.versions when you need to debug a native-binding issue.
What about Volta and n?
Same idea as nvm. volta pin [email protected] writes the version to package.json. n 20.11.1 switches the current shell. Both are fine; pick one and stick with it.
What if my CI runs a different Node than my laptop?
You are missing a .nvmrc or your CI is not reading it. Fix both. Add node-version-file: '.nvmrc' to actions/setup-node, or set image: node:20.11.1 in your CI job.
How do I know which Node version to pick?
Pick the latest active LTS unless you have a reason to be on a specific version. Active LTS gets security patches and bug fixes for 30 months. Maintenance LTS gets security patches only for 18 more months. End-of-life versions get nothing — bugs are your problem.
FAQ
What is the difference between node --version and node -v?
Nothing. Both print the version and exit. -v is shorter.
How do I check the version of an older Node I have installed?
nvm ls lists every installed version. nvm use 18.20.4 switches to the one you want.
What is the LTS version right now?
Node releases a new major every six months and an LTS every October. Node 22 is active LTS, Node 20 is maintenance LTS, Node 18 has reached end-of-life.
Why does my .nvmrc say one thing and node --version say another?
Because nvm use is opt-in. Install shell integration that auto-switches on cd into a directory with .nvmrc.
Can I check the Node version from inside a running app?
Yes. process.version returns the Node version string. process.versions returns an object with node, v8, uv, openssl, and others.
What about Volta and n?
Same idea as nvm. volta pin [email protected] writes the version to package.json. n 20.11.1 switches the current shell.
What if my CI runs a different Node than my laptop?
You are missing a .nvmrc or your CI is not reading it. Fix both. Use actions/setup-node with node-version-file: '.nvmrc'.
How do I know which Node version to pick?
Pick the latest active LTS unless you have a reason to be on a specific version. Active LTS gets security patches and bug fixes for 30 months.