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.
The "approaching memory threshold" warning — next dev only#
Instead of the plain "running out of memory" message, you may see this in your dev logs:
⚠ Server is approaching the used memory threshold, restarting...This comes from Next.js's own start-server.ts: after every request, it checks v8.getHeapStatistics() and, once used_heap_size crosses 80% of heap_size_limit, logs this warning and calls process.exit(77) in the same step — the warning and the restart are the same event, not a lead time to react in. This guard only runs when isDev is true: it is wired into next dev's request handler and does not exist on a production Node server (next start or a custom server). In production there is no equivalent warning; the process just runs until V8 itself throws FATAL ERROR: ... JavaScript heap out of memory and crashes, and whatever restarts it is your process manager or platform, not Next.js.
The 80% threshold is not configurable directly, but heap_size_limit follows max-old-space-size if you set it. Raising the limit with NODE_OPTIONS=--max-old-space-size=4096 moves the absolute usage at which the warning fires, not the 80% ratio itself.
What to do when you see it (dev only):
-
Check whether the threshold crossing recurs. The process exits when this warning is emitted, so compare measurements across a fresh run of the same workload. A single crossing does not by itself prove a persistent leak.
-
If it repeats, treat it as a leak signal. Run the server with
--inspectbefore reproducing the flow, so the inspector is already attached when the restart happens — the three-snapshot technique from the heap snapshot diagnosis still applies, but there is no separate warm-up window after the message appears. -
Check your module-level caches. The most common cause of a climbing heap that crosses 80% is an unbounded
MaporSetat module scope. The common app-level leaks section lists the usual suspects. -
In production, instrument instead of waiting for a warning. The
process.memoryUsage()logging snippet from the diagnosis section is what tells you the heap is climbing there, since no Next.js warning will. Set an alert onheapUsedat, say, 80% of your configured limit so you can investigate before the process is killed.
The warning accompanies the development restart; it does not provide a separate interval in which to intervene. Attach diagnostics before reproducing the workload and distinguish that development guard from a production V8 out-of-memory crash. For routes that genuinely need a large working set, review the production levers rather than treating this message as a production monitoring signal.
See also: Next.js 15 Middleware: Auth, Rate Limiting & Edge Guide.
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Related fixes & guides
- Fix: Next.js middleware not triggering
- Fix barrel_optimize Build Warnings with MUI in Next.js
- Fix: NextRouter was not mounted
- Next.js + Supabase Data Fetching: Server Components
- Next.js Server Actions with Supabase: Complete Guide
- NextJS Warning: Extra attributes from the server – Fix 2026
- Fix Next.js: Unable to Acquire the Dev Server Lock
- Fix UnhandledSchemeError: Reading from "node:buffer"
- Next.js Turbopack Stuck on Compiling? 5 Fixes (2026)
- Fix Turbopack's Wrong Workspace Root Warning

