Next.js

Fix 'Next.js 15 requires Node.js 18.18' Build Error

Next.js 15 drops support for older Node versions. Here is how to upgrade to Node.js 18.18.0 or later and fix build errors.

Mahdi Ben RhoumaMahdi Ben Rhouma
Updated
8 min read

You run next dev, next build, or next start and the process halts immediately with a hard stop in the terminal — no subtle bug, no partial compile:

text
You are using Node.js 16.20.0. For Next.js, Node.js version >= v18.18.0 is required.

That is the wording printed by next@15.0.0. The text changes between minor releases: from next@15.5 onwards the same check prints the full supported range instead:

text
You are using Node.js 16.20.0. For Next.js, Node.js version "^18.18.0 || ^19.8.0 || >= 20.0.0" is required.

Next.js 15 is enforcing a hard requirement for Node.js 18.18.0 or newer. The fix is to upgrade your runtime with a version manager like nvm or volta and pin the engines field in package.json so nobody regresses it. This behavior is consistent across local development environments, Docker containers, and CI/CD pipelines like GitHub Actions. If you are deploying to Vercel, the platform usually handles this automatically, but self-hosted deployments or custom CI setups will fail until the runtime is updated.

What the version check actually does#

Next.js 15 dropped support for older Node.js releases and pinned the minimum to the 18.18.0 LTS baseline. The official Next.js 15 release notes list this explicitly under breaking changes: "The minimum required Node.js version has been updated to 18.18.0." Older 18.x patch releases and Node 16 are simply outside the supported range — treat it as a hard supported-versions floor the framework enforces, not a per-API quirk. The practical consequence is the same regardless of the deeper reason: you must run Node.js 18.18.0 or newer.

The check lives in the next CLI entry point (dist/bin/next). At startup, next reads process.versions.node and calls semver.satisfies against its required range; in next@15.5 that range is ^18.18.0 || ^19.8.0 || >= 20.0.0. If the check fails, the CLI prints the message above and exits. It runs before your application code loads, so no configuration flag or polyfill in your code can bypass it.

First, confirm the diagnosis:

bash
node -v

You will likely see a version like v16.x.x or v18.17.0.

Upgrading with nvm or Volta#

Relying on the system default Node version is risky for production engineering. Use a version manager to pin the correct version for your project.

nvm (macOS / Linux)#

nvm is the standard tool. Run the following commands to install the latest Long Term Support (LTS) version of Node 18, which satisfies the 18.18.0 requirement:

bash
nvm install 18
nvm use 18
nvm alias default 18

The alias default command ensures that new terminal sessions automatically use Node 18.

Volta (cross-platform)#

Volta is an alternative: a cross-platform version manager that switches Node versions per project.

bash
volta install node@18

volta install sets the default Node version for your machine. To pin the version for the project, run volta pin node@18 inside it: Volta writes a "volta" key into package.json, and anyone with Volta installed who clones the repo gets that version automatically (Volta docs).

Pin the version with engines, .nvmrc, and tool files#

Upgrading your own machine is not enough — the next developer (or your CI server) can still run the project on Node 16. Three files prevent that:

engines in package.json:

json
{
  "name": "my-nextjs-app",
  "version": "1.0.0",
  "engines": {
    "node": ">=18.18.0",
    "npm": ">=9.0.0"
  }
}

Yarn classic already checks engines during yarn install and fails on a mismatch unless you pass --ignore-engines (Yarn install docs). npm only warns by default; add engine-strict=true to .npmrc and npm refuses to install a package that declares an incompatible engines range (npm config docs).

.nvmrc or .node-version: place a file named .nvmrc in your project root containing the string 18. When you run nvm use, it automatically switches to that version.

The "volta" key in package.json or .tool-versions: if you use Volta or asdf, commit the pin (volta pin node@18 writes it to package.json; asdf reads .tool-versions). These pins select the right Node version on machines that run the tool; the engines check above is what makes installs fail elsewhere.

These pin files sit at the project root, next to package.json. For how to organise the rest of a growing App Router codebase (feature folders, shared UI, route groups), see How to Structure Your Next.js App Router Project for Scale.

Reinstall dependencies after switching versions#

Changing Node versions can sometimes break native dependencies or binaries inside node_modules. It is best practice to regenerate your lockfile and dependencies after a major version upgrade:

bash
rm -rf node_modules package-lock.json
npm install

At this point Next.js 15's startup logic will detect a valid process.version, allowing the server initialization to proceed past the version check and load your application configuration. Verify:

bash
npm run dev

You should see output similar to this:

text
  ▲ Next.js 15.0.0
  - Local:        http://localhost:3000
  - Environments: .env.local
 
 ✓ Ready in 1.2s

The "Ready" message confirms that the version check passed and the server compiled successfully. If you are using TypeScript, also run npm run build to ensure there are no type errors related to the Node types (sometimes @types/node needs updating as well).

Local works, CI fails: setup-node still on an old default#

Even if your local machine works, your GitHub Actions or GitLab CI might fail if the actions/setup-node step defaults to an older version. Update your workflow YAML file to explicitly request Node 18 or 20:

yaml
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

Making CI the "source of truth" also catches drift in the other direction: if the build passes in CI but fails locally, it signals a local environment problem, not a project one. For more on Next.js and Supabase builds and deployment, see the Next.js + Supabase resource hub.

Docker images pinned to node:16#

If you are deploying via Docker, your base image might be pinned to an old version (e.g., node:16-alpine). Update your Dockerfile to use a newer base image:

dockerfile
FROM node:18.18-alpine AS base
 
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

Always verify the specific tag exists on the Docker Hub to avoid build failures during deployment. For more on production deployment strategies, see Deploying Next.js + Supabase to Production.

Upgrade checklist#

  • Run node -v to check your current version.
  • Upgrade to Node 18.18.0+ using nvm, volta, or the official installer.
  • Add "engines": { "node": ">=18.18.0" } to package.json.
  • Delete node_modules and reinstall dependencies.
  • Update CI/CD workflows (actions/setup-node) to use Node 18+.
  • Update Docker base images to node:18 or node:20.
  • Run npm run dev and npm run build to verify success.

Further reading: How to Change the Port in Next.js (and Fix EADDRINUSE).

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.