To check the version of Node.js, run node -v (or node --version) on the command line, and the runtime prints the current version. That is the working answer. The five gotchas come from the environments around the command: the path that runs an old binary, the package manager that disagrees with the binary, the runtime that needs a programmatic check, the dual install where node and nodejs point to different versions, and the version manager that was supposed to make this trivial in the first place.
This post walks through the five commands, the four most common gotchas (PATH, package manager, version manager, CI), the programmatic check for application code, and the deploy platform story that bakes the right version into the build.
Table of contents
- The five commands and what they actually print
- The PATH gotcha —
node -vandnodejs -vprint different things - The package manager gotcha — npm 10 with node 20
- The programmatic check —
process.versionsin a real script - The version manager gotcha — nvm, fnm, volta
- The CI gotcha — GitHub Actions, GitLab CI
- How this fits the rest of the stack
- FAQ
The five commands and what they actually print
The first command is node -v. It prints the version of the node binary on the PATH. The version looks like v20.11.0 — the v prefix is part of the convention. The --version flag is identical: node --version prints the same line. The two forms exist because node -v is faster to type and node --version is faster to read in scripts.
The second command is npm -v (or npm --version). It prints the version of the package manager, which is the version of npm that shipped with the current node. The two are not the same — node 20 ships npm 10, but the team can have node 20 and a different npm if they installed npm globally with npm install -g npm@latest. The right answer is to check both: node -v for the runtime, npm -v for the package manager.
The third command is process.versions in a node script. It returns an object with the version of node, v8, uv, zlib, and the other built-in libraries. The right answer is to use this in scripts that need to assert a minimum version:
if (parseInt(process.versions.node.split('.')[0]) < 18) {
throw new Error('Need Node 18+');
}
The command line is for the developer; process.versions is for the application.
The fourth command is nvm list (or nvm ls). The nvm version manager stores multiple node versions in ~/.nvm/versions/node/, and the current one is the one nvm is pointing to. The command prints the installed versions, the default version, and the currently active version. The right answer for a developer who runs multiple projects with different node versions is nvm use <version> followed by node -v to confirm.
The fifth command is npx -y node@<version> -v. It downloads the requested node version, runs it from the npx cache, prints the version, and exits. The right answer for a CI script that needs to confirm a specific version without installing it is this — it is the version check that does not pollute the host. The gotcha is that the download is slow on first use, so the script should cache it.
The PATH gotcha — node -v and nodejs -v print different things
On some Linux distributions (notably Debian, Ubuntu, and the ARM-based variants), the binary is installed as nodejs to avoid clashing with a different package called node (an amateur radio tool). The team types node -v and gets command not found — but nodejs -v prints the version. The fix is either to install a node symlink (ln -s /usr/bin/nodejs /usr/local/bin/node) or to use the correct binary name. The right answer is the symlink: every tutorial, every tool, and every script assumes node, not nodejs.
On macOS, the team that installs node via Homebrew gets a single node binary at /opt/homebrew/bin/node (Apple Silicon) or /usr/local/bin/node (Intel). The path puts it first, and node -v prints the homebrew version. The team that installs node via the official installer gets /usr/local/bin/node from the .pkg, and the homebrew version may shadow it. The right answer is to run which node to confirm the binary, then node -v to confirm the version.
On Windows, the team that installs node via the .msi gets a node.exe in C:\Program Files\nodejs\. The team that installs via nvm-windows gets nvm.exe and a per-version folder. The PATH must include the binary’s folder, and the team’s node -v reflects the version on the PATH. The gotcha is the AppData\Roaming\npm path for global tools: a global npm tool may run with a different node than the team’s node -v.
The package manager gotcha — npm 10 with node 20
The team that runs npm install -g npm@latest upgrades npm to whatever the current version is, which is decoupled from the node version. The team’s node -v still says v20.11.0, but npm -v now says 10.5.0 (or whatever shipped that week). The gotcha is that some older packages assume a specific npm version, and a global npm upgrade can break them. The right answer is to use a node version manager that ties the node version to a specific npm: nvm install 20 ships with npm 10, nvm install 18 ships with npm 9.
The team that uses pnpm or yarn has a different version line. pnpm -v prints the pnpm version, not the npm version. The right answer for a project that uses pnpm is to add a packageManager field to package.json ("packageManager": "[email protected]") and use corepack to enforce it. The team that uses yarn 1, yarn 2, yarn 3, or yarn 4 has four different version lines — yarn classic is 1.x, yarn berry is 2.x+ — and yarn -v prints the active one.
The programmatic check — process.versions in a real script
The command line check is for the developer. The application needs to check programmatically: the right answer is process.versions.node, which returns the node version as a string ('20.11.0'). The common pattern is to assert a minimum version in a startup script:
const major = parseInt(process.versions.node.split('.')[0], 10);
if (major < 18) {
console.error(`Node 18+ required. Current: ${process.versions.node}`);
process.exit(1);
}
The gotcha: the team that uses engines in package.json ("engines": { "node": ">=18" }) relies on npm/yarn to warn or fail on install, but the runtime check is still needed for the production case. The engines field is honored by npm 7+ on install, but the team’s CI or production runtime may not enforce it. The right answer is both: engines in package.json for the dev-time signal, process.versions check in the app for the runtime signal.
The advanced check is process.versions as a whole. The team’s app may need a specific V8 version (process.versions.v8), a specific uv version (libuv, used for async I/O), or a specific OpenSSL version (process.versions.openssl). The right answer for a crypto-heavy app is to check the OpenSSL version at startup — the app’s behavior may differ between OpenSSL 1.1.1 and 3.0 — and to fail early with a clear error message.
The version manager gotcha — nvm, fnm, volta
The team that uses nvm (Node Version Manager) has multiple node versions installed. The currently active version is the one nvm is pointing to, which is set by the most recent nvm use <version> or by the .nvmrc file in the current directory. The right answer is to commit a .nvmrc with the project’s required version (18.19.0) and have every developer run nvm use to align. The gotcha is that nvm use does not persist across shells — the team that opens a new terminal may have a different default.
The team that uses fnm (Fast Node Manager) gets the same multi-version behavior with a Rust-based binary. The commands are similar (fnm use 20, fnm list, fnm default 20). The gotcha is that fnm does not auto-switch on cd by default — the team needs a shell hook (fnm env --use-on-cd) or the use-on-cd: true config.
The team that uses Volta gets version-pinned project state via package.json. The team runs volta pin node@20 and Volta writes the version to package.json under volta. The right answer is Volta for the team that wants version state to travel with the project, not with the developer’s shell. The gotcha is that Volta is a single-binary install with limited platform support (macOS and Linux, not native Windows).
The team that does not use a version manager and runs nvm install followed by apt install nodejs ends up with two node versions on the PATH, and the system’s node wins. The right answer is to uninstall one of them. The team’s node -v may print the system version (older) while the team’s nvm use 20 switches to a newer version that is not on the PATH for the next shell.
The CI gotcha — GitHub Actions, GitLab CI
On GitHub Actions, the actions/setup-node@v4 step defaults to whatever node version is in the team’s .nvmrc (if it exists), or the version the team specifies. The right answer is to pin both: actions/setup-node@v4 with node-version-file: '.nvmrc' plus the .nvmrc in the repo. The gotcha is the difference between node-version and node-version-file: the file version reads from .nvmrc, the explicit version takes a string.
On GitLab CI, the image: node:20 directive pulls the official node image with node 20 installed. The team’s node -v in the CI job prints 20.x.x. The right answer is to pin the minor version (image: node:20.11-bookworm) for reproducibility, or to use the major version for speed of the image pull. The gotcha is that the image: tag can change (Debian slim vs. Alpine vs. full), and the team’s native modules may break on a different base.
The team’s CI may run on a different node than the dev machine. The right answer is to add a CI step that prints node -v and npm -v at the start of every job — the output goes into the CI log, the developer can see what the CI actually used. The wrong answer is to assume the CI uses the same version as package.json engines — the engines field is a hint, not a guarantee.
How this fits the rest of the stack
The version check is a small piece of a larger pattern: the team’s runtime, package manager, OS, and deployment platform all have versions, and the team’s mental model for the project is the sum of those versions. The right answer is to commit the versions that matter (.nvmrc for node, package.json#engines for node+npm, Dockerfile FROM node:20-bookworm-slim for the runtime base) and to print the versions in the deployment logs. The RunxBuild hosting calculator is the right place to do the broader exercise — pick the runtime, the memory tier, the deployment frequency, the team size, and the egress, and the calculator shows what the project’s monthly infra actually costs.
Useful related references:
FAQ
What is the command to check the version of Node.js?
node -v or node --version on the command line. Both print the same line in the form v20.11.0.
What is the difference between node -v and npm -v?
node -v prints the version of the node runtime. npm -v prints the version of the npm package manager, which may differ if the team upgraded npm globally. The two are not the same.
Why does node -v print command not found on Linux?
On Debian/Ubuntu and some ARM Linux distributions, the binary is installed as nodejs to avoid clashing with a different package called node. Use nodejs -v, or add a symlink: ln -s /usr/bin/nodejs /usr/local/bin/node.
How do I check the Node.js version programmatically?
Use process.versions.node in a node script. It returns the version as a string ('20.11.0'). The team can assert a minimum version with parseInt(process.versions.node.split('.')[0], 10).
How do I check the version of node on Windows?
Open PowerShell or Command Prompt and run node -v. If the command is not found, the PATH does not include C:\Program Files\nodejs\. Add it to the system PATH and reopen the terminal.
How do I list all installed node versions with nvm?
nvm list (or nvm ls). It prints every installed version, the default version (with an arrow), and the currently active version. nvm use <version> switches the active version for the current shell.
How do I check the node version in a CI script?
Add a step at the start of the job that runs node -v and npm -v. The output goes into the CI log and confirms the actual versions the CI used. Pin the version in the CI config (e.g., node-version-file: '.nvmrc' in GitHub Actions).
How do I check the node version in a Docker container?
The Dockerfile’s FROM node:20-bookworm-slim line sets the runtime version. To confirm at runtime, exec into the container (docker exec -it <container> /bin/sh) and run node -v. The version in the running container matches the tag in the FROM line.