Print the versions and diff two build logs
Add the version line to the build command, then compare the last green log with the failing one. On Netlify, if the build still misbehaves after you change a version, redeploy with the clear cache option. For a function's runtime, log process.version inside the function.
node -v && npm -v && npm ci && npm run build
Pin the runtime where the host reads it
Use an exact version on hosts that accept one: NODE_VERSION on Netlify, Cloudflare Pages and Render, or a .nvmrc or .node-version file. Vercel offers only majors, so pin the major in Project Settings or in engines. Cloudflare Pages build image v3 ignores engines, and Render advises a range with an upper bound.
{ "engines": { "node": "22.x" } }
Install from the lock file and commit it with every dependency change
npm ci errors if package.json and the lock file disagree, never writes to either, and deletes node_modules first. Use it in CI and in your Dockerfile. If you generated the lock file with flags such as --legacy-peer-deps, pass the same flags to npm ci, or commit them in .npmrc.
COPY package.json package-lock.json ./
RUN npm ci
Pin base images by tag and digest, and package managers exactly
Put the digest after an exact tag so a moved tag cannot change your build. Install CLIs as exact dev dependencies instead of calling whatever npx finds. For pnpm or Yarn on Netlify, set packageManager to an exact version, because Corepack does not accept ranges there. Pin apt packages too, and keep apt-get update and install in the same RUN.
FROM node:<exact-version>-slim@sha256:<digest>
# npm install --save-dev --save-exact <deploy-cli>
Run a clean install on every runtime you deploy to
A matrix job gives each Node version its own clean checkout and npm ci. Peer conflicts and missing native binaries show up here instead of in production.
strategy:
matrix:
node-version: ['22.x', '24.x']
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm test
Upgrade one thing per change, on a schedule
Pinning without updates only delays the break, and Docker's docs note that digest pinning opts you out of automated security fixes. Let Dependabot open pull requests for npm, Docker and Actions. Merge each toolchain, CLI or image bump on its own, so a failure has one suspect.
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"