← Back to Fixes

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:

plaintext
The server is running out of memory, restarting to free up memory

Routes 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#

  1. 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.
  2. 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 Map that never gets pruned.
  3. 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 / generateStaticParams job, 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:

bash
# 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 build

This 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:

bash
next dev --turbo

Turbopack'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:

bash
NODE_OPTIONS='--inspect' npm run dev
# or for a production Node server:
NODE_OPTIONS='--inspect' node server.js

Open chrome://inspectinspectMemory tab → Heap snapshot. Use the three-snapshot technique:

  1. Snapshot A — after the app warms up.
  2. Snapshot B — after running the leaking flow once.
  3. 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:

js
// 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. A const 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 setInterval or an event emitter you attach on every request and never removeListener / 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: true or 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:

  1. 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.
  2. Stream large responses. Do not await an entire dataset into memory then JSON.stringify it. Stream it. Response with a ReadableStream, or the streaming render APIs, keep the working set small.
  3. Prune dependencies. next build shows 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 with await import().
  4. Disable sourcemaps on the server. They are for dev and error reporting; shipping them on the server inflates resident memory.
  5. 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.
  6. 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-size set 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().heapUsed so 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=4096 to 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 fixes & guides