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.
Photo by Pankaj Patel on Unsplash
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.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. 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 framework checks the process.version string at startup. If the semantic versioning comparison fails, it throws the error you see. This check happens before the application code even loads, meaning you cannot bypass it with configuration flags or polyfills in your code. The logic essentially parses your Node version string and ensures it is greater than or equal to 18.18.0. This enforcement ensures that the underlying runtime can handle the concurrent features and edge runtime optimizations required for the App Router.
The requirement also reflects a broader shift: Next.js 15 leverages the native fetch API available in Node 18, removing the need for polyfills like node-fetch. This reduces bundle size and improves performance, but it requires a runtime that supports these web standards natively.
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)#
For a faster, tool-agnostic manager that works across platforms, I prefer Volta. It manages Node versions without changing your shell profile.
volta install node@18Volta automatically pins the version in a volta.json file in your project directory, ensuring anyone who clones the repo gets the right Node version immediately.
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"
}
}If you are using yarn, you might need to enable the engines-strict option in your .yarnrc file to make this check fail-fast during installation.
.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.
volta.json or .tool-versions: if using Volta or asdf, commit the configuration file that pins the tool version. This ensures that npm install fails or warns if the wrong Node version is active.
When structuring large-scale applications, managing these environment configurations becomes part of your core architecture. I discuss how to organize these config files alongside your app router structure in 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. I cover how to integrate this into a robust Supabase and Next.js pipeline in Next.js & Supabase Masterclass: Robust CI/CD Pipelines with GitHub Actions.
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.
Related#
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Continue Reading
Fix Dynamic Server Usage Error in Next.js App Router
Next.js throws this when a route it wants to render at build time calls a dynamic function — cookies(), headers(), searchParams, or a no-store fetch. The fix is either opt the route into dynamic rendering, or remove the request-time dependency if it should be static.
How to Change the Port in Next.js (and Fix EADDRINUSE)
Change the default Next.js port when it collides with another process. Step‑by‑step fix with code, verification, and prevention tips.
useSearchParams Suspense Error: 3 Real Fixes (Next.js 15)
The error only surfaces during the production build — dev mode hides it completely. Here is why, and the three fixes ranked by situation.
Browse by Topic
Find stories that matter to you.
