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.
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:
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:
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:
node -vYou 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:
nvm install 18
nvm use 18
nvm alias default 18The 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.
volta install node@18volta 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:
{
"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:
rm -rf node_modules package-lock.json
npm installAt 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:
npm run devYou should see output similar to this:
▲ Next.js 15.0.0
- Local: http://localhost:3000
- Environments: .env.local
✓ Ready in 1.2sThe "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:
- 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:
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 ciAlways 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 -vto check your current version. - Upgrade to Node 18.18.0+ using
nvm,volta, or the official installer. - Add
"engines": { "node": ">=18.18.0" }topackage.json. - Delete
node_modulesand reinstall dependencies. - Update CI/CD workflows (
actions/setup-node) to use Node 18+. - Update Docker base images to
node:18ornode:20. - Run
npm run devandnpm run buildto verify success.
Further reading: How to Change the Port in Next.js (and Fix EADDRINUSE).
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
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.
Continue Reading
Fix Turbopack Build Errors from a Webpack Config
Find the plugin behind the unexpected webpack hook, choose a compiler deliberately, and verify that the behaviour you need still runs.
How to Change the Port in Next.js (dev and start)
Change the default Next.js port when it collides with another process, and know why next dev sometimes moves to 3001 on its own while next start crashes.
Fix: FetchError request to localhost:1337 ECONNREFUSED ::1
The exact fix for the ECONNREFUSED ::1:1337 error between Next.js and a local Strapi API, and why it depends on your Node version.


