Next.js "Server Is Running Out of Memory" Fix (2026)
Next.js restarts with "server is running out of memory"? Find the real leak: the Turbopack dev fix, heap snapshots, and production memory levers.
Shortcut: paste your failing output into the free Next.js Build Error Decoder — it recognizes the heap-out-of-memory signature (and 12 others) and links straight to the fix that applies.
The message#
You are running next dev (or a Node production server), the terminal prints
this, and the server restarts:
The server is running out of memory, restarting to free up memoryRoutes reload slowly, sessions drop, and in dev your HMR stops working for a few seconds every time it fires. This was filed as vercel/next.js#46756 (dev App Router + RSC accumulation) and tracked as NEXT-1353, with the production side in vercel/next.js#49929 (NEXT-1314, high memory in deployed projects).
The message is a symptom, not a diagnosis. Next.js notices the Node heap crossed its limit and restarts to recover. Your job is to find out why it crossed — and there are only three possible buckets.
The three causes#
- Dev-mode accumulation — the classic App Router + RSC hot-reload leak in Next.js 13.x. Repeated refreshes grow the heap until it restarts. This is a tooling problem, not your code.
- A real leak in your app or deps — an in-memory cache without eviction,
event listeners never removed, a closure holding a growing dataset, a
Mapthat never gets pruned. - A legitimate heap that is too big for the limit — a route that loads a
large dataset into memory at build time, a heavy
getStaticProps/generateStaticParamsjob, or sourcemaps in production.
You diagnose by bucket. The fixes are different.
First, stop the restarts (band-aid)#
Raise the V8 heap limit so you can actually work while you investigate:
# 4 GB heap (dev)
NODE_OPTIONS=--max-old-space-size=4096 npm run dev
# 8 GB heap (large production builds)
NODE_OPTIONS=--max-old-space-size=8192 npm run buildThis is not a fix. It is the room you need to take a heap snapshot instead of getting restarted out of the inspector. Set it, then move to diagnosis.
If it only happens in dev — switch to Turbopack#
The 13.x dev-mode leak in webpack's App Router pipeline is the single most common cause of this message. If you are on Next.js 14+ and still hitting it in dev, try Turbopack first:
next dev --turboTurbopack's module graph does not accumulate on hot reload the way webpack did, so for the classic dev leak it resolves immediately. This is the lowest- effort, highest-hit fix in this whole article.
If you are on an older 13.x and cannot move to Turbopack, upgrade to the latest stable — the dev accumulation was steadily reduced across 13.x and largely resolved in 14+. Do not stay on a leaking canary.
Diagnose a real leak with a heap snapshot#
If Turbopack did not help, or the leak is in production, take a heap snapshot. Run the dev server under the inspector:
NODE_OPTIONS='--inspect' npm run dev
# or for a production Node server:
NODE_OPTIONS='--inspect' node server.jsOpen chrome://inspect → inspect → Memory tab → Heap snapshot.
Use the three-snapshot technique:
- Snapshot A — after the app warms up.
- Snapshot B — after running the leaking flow once.
- Snapshot C — after running it again.
In the comparison view (C vs B vs A), objects that grow between every snapshot are your leak. Filter to "retained size" and look for your own domain objects (users, rows, requests) — if you see your own types growing, the leak is in your code. If you only see framework / node internals, the leak is upstream (or you are holding references to your data in a structure you forgot about).
In server.js, log the heap on an interval to catch growth across requests:
// server.js (or a small instrumentation file)
setInterval(() => {
const m = process.memoryUsage();
console.log(
`rss=${(m.rss / 1048576).toFixed(0)}MB ` +
`heapUsed=${(m.heapUsed / 1048576).toFixed(0)}MB ` +
`heapTotal=${(m.heapTotal / 1048576).toFixed(0)}MB ` +
`external=${(m.external / 1048576).toFixed(0)}MB`,
);
}, 10_000);A flat line means no leak. A steady climb that never settles means a leak.
Common app-level leaks#
The usual suspects, in order of how often I have seen them:
- Module-level
Map/Set/ array caches. Aconst cache = new Map()at module scope that you write to on every request and never evict from. Fix: bound it (an LRU), move it to a real cache (Redis / KV), or recompute. - Event listeners never removed. A
setIntervalor an event emitter you attach on every request and neverremoveListener/clearInterval. Each request leaks the listener and everything it closes over. - Closures holding large datasets. A request handler that builds a giant array inside a closure passed to a long-lived callback. The array lives as long as the callback does.
- Sourcemaps in production.
productionBrowserSourceMaps: trueor a misconfigured build keeps large source content in memory. Disable for the Node server, keep them only for client error reporting. - Heavy
generateStaticParams/ build-time fetches. Pulling a large dataset into memory at build time to enumerate pages. Stream it from disk or a DB cursor instead.
Production levers#
For deployed Next.js (Node server or serverless), memory is a resource you budget for. The levers, biggest first:
- Move large in-memory caches out of the process. Anything that grows
with traffic belongs in Redis, Upstash, or your platform's KV, not in a
Map. This is the single largest production win. - Stream large responses. Do not
awaitan entire dataset into memory thenJSON.stringifyit. Stream it.Responsewith aReadableStream, or thestreamingrender APIs, keep the working set small. - Prune dependencies.
next buildshows the largest server modules. A heavy dep loaded into the server bundle sits in memory for the life of the process. Lazy-import the rare ones withawait import(). - Disable sourcemaps on the server. They are for dev and error reporting; shipping them on the server inflates resident memory.
- Raise the serverless memory budget. On Vercel, serverless functions have a per-function memory ceiling (1 GB on Hobby, more on Pro — confirm on your plan). A route that genuinely needs more memory should be split out and given its own configuration rather than dragging the whole app up.
- Use the Edge Runtime for stateless routes. Edge functions have a smaller, fixed memory profile and no Node heap growth; move the routes that are pure request/response work over. For the gotchas of what you cannot run on Edge (no native TCP, no sync FS), the dynamic server usage / could not be rendered statically fix covers the boundary.
The dev-vs-prod split#
A leak that only reproduces in next dev and never in next build / next start is almost always the webpack dev pipeline. Do not spend two days
hunting a leak in your code that is actually webpack's hot-reload graph —
try Turbopack for thirty seconds first.
A leak that reproduces in next start against a production build is your
code or your deps. Take the three-snapshot path above.
And the failure mode that is not a leak at all: the build passes locally, you deploy, and it breaks in production for a different reason. That is the build passes locally, broken in production postmortem, not this one. They look similar from the outside and have completely different causes.
Production checklist#
-
NODE_OPTIONS=--max-old-space-sizeset for the build and the server. - No module-level unbounded caches; every cache has an eviction policy or lives outside the process.
- No
setInterval/ listeners without a matching teardown. - Sourcemaps disabled on the Node server.
- Large routes streamed, not buffered.
- Heavy routes split out with their own memory budget if on serverless.
- Stateless routes on Edge where possible.
- An alert on
process.memoryUsage().heapUsedso you see growth before the restart message does.
For the broader performance posture — what to measure, what to move to Edge, how to keep the bundle thin — the Next.js performance optimization guide is the longer read. For the deploy-time failure modes that look like memory issues but are not (middleware not running, static assets 404), see middleware not running in Vercel production and the deploying Next.js + Supabase to production guide.
TL;DR#
- Restarting on memory? Raise the heap with
NODE_OPTIONS=--max-old-space-size=4096to stop the bleeding. - Only in dev? Switch to
next dev --turbo— the classic 13.x webpack leak is the most common cause and Turbopack sidesteps it. - In production? Take three heap snapshots, find what grows, move caches out of the process, stream large responses, disable server sourcemaps.
Related Articles#
- Next.js Build Passes Locally, Broken in Production
- Next.js Performance Optimization Guide
- Fix "Dynamic Server Usage: Could Not Be Rendered Statically"
- Next.js Middleware Not Running in Vercel Production
- Deploying Next.js + Supabase to Production
- Fix Next.js Hydration Mismatch in App Router
- Fix Missing Suspense Boundary with useSearchParams
Related fixes & guides
- Next.js useSearchParams Suspense: Static Rendering Fix 2026
- Fix barrel_optimize Build Warnings with MUI in Next.js
- Next.js Scroll Resets When searchParams Change
- Next.js Data Fetching Patterns with Supabase: Server
- Caching Strategies for Next.js + Supabase Applications
- Next.js Server Actions with Supabase: Complete Guide
- Fix 'Next.js 15 requires Node.js 18.18' Build Error
- Next.js Turbopack Stuck on Compiling? 5 Fixes (2026)
- Fix Next.js revalidatePath Not Working in Server Actions
- Fix: react-dom/server Import Error in Next.js App Router