Originally published at xabierlameiro.com.
If your self-hosted Next.js server grows until it dies with FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory - or the container gets OOMKilled (exit code 137) - don't start by auditing your own code. As of July 2026 there are three documented memory leaks open in the framework itself, spanning Next.js 15.5 to 16.3.
Leak 1: the router LRU cache doesn't count its keys
#94890 - the route-resolution cache's size function adds up the value strings but never the URL key, so it can retain ~1M keys (hundreds of MB) while believing it is tiny. Signature: slow heap drift over days that tracks unique URLs served (bot crawls, long slugs), not request volume; heap-snapshot retainer traces end at LRUNode. Tip: chart your heap against distinct routes, not requests per second.
Leak 2: the RSC render tree is retained on client aborts
#94919 - streams that don't finish normally (mobile clients, crawlers, users navigating away) pin the whole element tree via the Flight request's AbortController. Much stronger on Node 22/24 than 20; the reporter measured ~2 MB per request on heavy pages. Mitigation while it stays open: trim the RSC payload of your heaviest pages.
Leak 3: middleware setTimeout ids retained by the sandbox
#95094 - the sandbox TimeoutsManager only releases a timeout id on explicit clearTimeout. A one-line workaround that works today:
const timeoutId = setTimeout(() => {
fireAndForgetWork();
clearTimeout(timeoutId); // releases the id from the sandbox manager
}, 0);
Which one are you hitting?
| Growth shape | Correlates with | Suspect |
|---|---|---|
| Slow, monotonic drift over days | Unique URLs (bots, long slugs) | Router LRU cache (#94890) |
| Proportional to traffic; worse on heavy pages | RSC payload and client aborts | RSC tree retention (#94919) |
| Steps the GC never recovers | Requests through middleware | Sandbox timeouts (#95094) |
| No OOM, but sporadic 504s on serverless | Render cost, not memory | Your render is expensive |
To confirm: start with NODE_OPTIONS='--inspect' next start, take two heap snapshots separated by load, and read the retainers in Chrome DevTools' Memory tab.
On serverless the leak wears a disguise
You don't get the OOM - you get 504s. My blog's tag pages were timing out with FUNCTION_INVOCATION_TIMEOUT because of an O(N^2) post loader (a ~32-second render against a ~10-second limit); instance recycling had been hiding the waste. The fix was a module-level cache: memory spent on purpose, bounded and static per deploy.
The full diagnosis flow - which retainer maps to which issue, the exact docs commands, and my 504 postmortem with before/after numbers - is in the original post: https://xabierlameiro.com/blog/nextjs/nextjs-memory-leak-in-production
Top comments (0)