Part of Hosting by frameworkHosting · Node.js

Pin the Node.js version for a deploy: put the major in engines.node.

Set the major version in engines.node in package.json, for example "24.x". Most platforms read it, but some prefer .nvmrc, .node-version or a NODE_VERSION variable, and they disagree on which wins. On Canner, engines.node and the project’s Node.js version setting decide. It offers Node 24 and 22, and ignores .nvmrc.

Facts checked

Which file each platform reads

The same repository can build on three different Node versions depending on where you push it. This is what each platform documents, highest priority first.

PlatformReads, in order of priorityIgnores or doesn’t documentVersions
Cannerengines.node when the project setting doesn’t satisfy it; otherwise the project’s Node.js version setting.nvmrc, .node-version, a NODE_VERSION variable24, 22 (22 is the default)
Vercel †engines.node in package.json, then the project setting.nvmrc isn’t mentioned26.x (default), 24.x, 22.x; majors only
Netlify †NODE_VERSION variable, .node-version or .nvmrc, then the UI setting, then the build image defaultengines isn’t mentionedAny version nvm can install
Render †NODE_VERSION, .node-version, .nvmrc, then engines—Default 24.22.1 for services created since 2026-10-21
Heroku †engines.node only.nvmrc and .node-version aren’t mentionedDefault 24.x
Railway (Railpack) †RAILPACK_NODE_VERSION, devEngines.runtime, engines.node, .nvmrc, .node-version, mise.toml or .tool-versions—Defaults to the current LTS

† Competitor details come from each vendor’s own published pages on the dates shown; prices and plans change, so check the vendor before you decide. Vercel (2026-10-29) · Netlify (2026-10-29) · Render (2026-10-29) · Heroku (2026-10-29) · Railway (Railpack) (2026-10-29)

What to put in the file

Pin a major: 24.x. A major gets security patches without surprising you. An exact version like 24.11.0 freezes you on old patches where the platform allows it, and many platforms only offer majors anyway. An open range like >=18 has no ceiling, so the day a platform adds a new major, your build may move to it. Heroku’s docs give the same advice.

Keep .nvmrc if your team uses nvm or fnm, but treat it as a local convenience and keep it equal to engines.node. When the two disagree, the platform reading one and your laptop reading the other is how “works locally” builds fail.

// package.json: the file deploy platforms read
{
  "engines": { "node": "24.x" }
}

# .nvmrc: for nvm and fnm on your machine (Canner ignores it)
24

How Canner picks the version

Canner reads engines.node from the package.json in your project’s root directory and compares it with the project’s Node.js version setting (Settings, default Node 22). It only compares majors. Each case prints its own line in the build log:

  • No `engines.node`: the setting is used. The log shows [canner] node version: 22.
  • The setting satisfies the range: the setting is kept. >=18 stays on Node 22 rather than jumping to 24, so a broad range never moves a working project to a new major.
  • The setting doesn’t satisfy it: Canner takes the newest major it offers that does, and says so: [canner] package.json engines.node="^24" isn't satisfied by Node 22; building on Node 24.
  • Nothing satisfies it: the build stops before installing: package.json engines.node="20.x" needs a Node version Canner doesn't offer (available: 24, 22). Widen or remove engines.node, or set the project's Node version and turn on "force". The same happens with 26.x, Vercel’s current default.
  • The range can’t be read: Canner keeps the setting and notes Couldn't read engines.node="…". A range with an upper bound written as <= is one example; write >=18 <23 instead of >=18 <=22.
  • Minor versions are not compared. >=22.17 counts as satisfied by Node 22. If a dependency needs a recent minor, choose Node 24.

Ignore engines.node on purpose

Sometimes a dependency declares a range your app doesn’t really need, or you want to test a major before changing package.json. The API has a node_version_forced field on the project: with it on, Canner builds on the setting and logs engines.node="…" ignored — this project pins Node 22. There is no dashboard switch for it yet.

# Use the project's version and ignore engines.node
curl -X PATCH https://api.canner.ca/v1/projects/my-app \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"node_version": "24", "node_version_forced": true}'

Why a build picks the wrong Node

  • The platform reads a different file than you edited. Netlify and Render read .nvmrc; Heroku documents only engines, and Canner doesn’t read .nvmrc. Check the table above.
  • A higher-priority source wins. On Render or Netlify, a NODE_VERSION variable set long ago in the dashboard beats your new .nvmrc.
  • The file is in the wrong directory. In a monorepo, the platform reads the package.json of the directory it builds. On Canner that is the project’s Root directory.
  • npm doesn’t enforce `engines`. Unless engine-strict is set, npm treats the field as advisory and only warns. Whether it changes the Node version is up to the platform, not npm.
  • The major is end-of-life or not offered. Node 20 is end-of-life. A platform that dropped it either refuses the build or moves you up.

Questions

Does Canner read .nvmrc or .node-version?

No. Canner reads engines.node in package.json and the project’s Node.js version setting. Keep .nvmrc for local tools if you like, set to the same major.

Which Node.js versions does Canner offer?

Node 24 and Node 22. New projects default to 22. The settings list comes from what is installed on the build servers, so it changes when a major is added.

Why is my engines field ignored?

npm treats engines as advisory unless engine-strict is set, and some platforms (Netlify, per its docs) choose Node from other files. On Canner it is only ignored when the project’s version is forced.

Should I pin an exact Node version like 24.11.0?

Usually not. A major range such as 24.x keeps security patches coming. Pin an exact version only to reproduce a bug, and only on a platform that installs exact versions.

Push a Node app and read which version it got.

Every build log names the Node version and why it was chosen. More guides are under hosting by framework.