Postmortem: The Next.js App Router Memory Leak of 2023
In 2023, self-hosted Next.js App Router apps were reported eating memory until the host killed them. Issue #49929 ran three months and 131 comments; it found a high process baseline and one canary-only Node.js fetch() leak.
Issue #49929 on vercel/next.js, titled "High memory usage in deployed Next.js project", opened on 2023-05-17 with a small reproduction: a Next.js 13.4.3-canary App Router project that behaved normally on Vercel but sat at around 220MB of memory on Fly.io, even under normal use. It closed on 2023-08-24 with 131 comments, screenshots from Kubernetes pods, Railway deployments and Fly.io machines, and Tim Neutkens of the Next.js team leading the investigation (with shuding and jridgewell also named in the thread), while Lee Robinson moderated and asked for reproductions. This is what the thread actually established, sourced from the issue itself, not from memory of "the App Router memory bug".
The symptom, stated precisely#
Most reports described memory that grew after deployment and was not given back, until the host's memory limit was reached and the process was killed. The reports were not uniform: some were from development mode (which the team redirected to #46756), and at least one commenter, nmengual, found that the Node.js runtime metrics stayed flat while the pod-level memory kept rising. Fly.io's free machines with 256MB of RAM came up repeatedly, because the original reproduction and two larger example apps from karlhorky were crashing there with out-of-memory errors.
The spread of the reports made this hard to dismiss as one team's misconfiguration: karl-run measured 10x the idle memory of his Pages Router branch on the same Kubernetes cluster for his App Router branch, before any requests; stx-chris reported that their App Router app "leaks memory overnight… Every page transition increases memory consumption without releasing it again", and that after a couple of hours the container ran out of memory and restarted.
Why it took three months to find#
Tim Neutkens' comments in the thread are the most useful part of it, because they explain why this kind of report is hard to diagnose. A rising Resident Set Size (RSS) graph, which is what nearly every screenshot in the thread showed, does not by itself prove a leak. In his 13 July comment he explained that the heap of a shared application was being garbage-collected correctly, and that V8 retains memory after a spike instead of returning it to the operating system immediately. A later update added that roughly 50% of the assigned memory was empty space V8 pre-allocates for future heap growth, and that the team was "fairly certain there is no memory leak for production in the latest version of Next.js".
The other half of the explanation was arithmetic. In a 16 July comment, Neutkens described the four processes Next.js booted when the App Router was enabled: the next start (or standalone server.js) process, a routing process, a renderer for the App Router and a renderer for the Pages Router (kept separate so each could resolve its own React version). Each took around 50MB of RSS when idle, so the baseline was about 200MB before any application code loaded, against what appeared to be a limit of about 330MB on Fly.io's small machines. No leak was needed to hit that ceiling.
That is why the investigation dragged: most reports were dashboard screenshots, and the team needed heap snapshots and a runnable reproduction to tell an actual leak apart from expected V8 behaviour. Neutkens wrote that about four engineering weeks had gone into the issue, and asked repeatedly for application code rather than another chart.
The two problems that were actually confirmed#
Two concrete problems were found and fixed. They are unrelated to each other, which is why no single patch "fixed the memory leak".
1. The process baseline. This was not a leak, but it was the main reason small instances ran out of memory. Neutkens landed PR #53523 (merged 2023-08-08), which merged the idle server process into the routing process and took the count from four to three. shuding's work then became PR #54143 (merged 2023-08-22), which merged the App Router renderer into the routing process, leaving two: one for routing and App Router rendering, one for Pages Router rendering. The extra processes ran whenever the App Router was enabled, which was the default in 13.4, so even a Pages-only project like broksonic21's paid for them unless it set experimental.appDir to false.
2. A real Node.js leak in undici's fetch(), exposed on canary. Investigating broksonic21's reproduction (a default create-next-app project under ab load), Neutkens found "a memory leak in Node.js when an AbortController signal is passed to fetch()", which Next.js does internally. The fix, undici PR #2049, was merged in April 2023 and reached Node.js through the undici updates in Node.js 18.17.0. Per Neutkens, it only affected Next.js canary builds that included the new stream cancellation (PR #52281, first stable in 13.4.13), running on Node.js below 18.17.0. It cannot explain the May to July reports. broksonic21 then confirmed that with Node.js 18.17.0 and next@13.4.13-canary.12 the full ab run completed, and ungarida reported that upgrading from 13.4.9 to 13.4.13 on a Pages Router app also required Node.js 18.17.0 or later.
A third popular workaround, disabling Image Optimization with images: { unoptimized: true }, correlated with lower memory for several commenters. Reports about sharp conflicted: one commenter suggested uninstalling it, others said removing it did not help, and one said installing it brought memory down. The team reduced the concurrency of its sharp integration, but never confirmed Image Optimization as the same bug. Instead it opened a dedicated issue, #54482, which is still open. Treating that workaround as "the fix" for this issue would be the wrong takeaway.
What actually shipped, and when#
- Next.js 13.4.10: released with what Neutkens described as "some improvements to memory usage increasing".
- Next.js 13.4.13: includes PR #53495, which fixed the "zombie process" where
next startkept running afterCtrl+C(first verified on13.4.13-canary.12). - Next.js 13.4.14: first stable release with PR #53523 (four processes down to three). From this version,
experimental.appDir: falseno longer has any effect. - Next.js 13.5.0: first stable release with PR #54143 (three processes down to two). It shipped on canary from
13.4.20-canary.2; there was never a stable 13.4.20. - Node.js 18.17.0 or later: required for the
undicifetch()fix, if your Next.js version includes stream cancellation (13.4.13 or later).
Is this still relevant if you're starting a project today?#
Directly, no: a project scaffolded with a current Next.js version is already past every change in this thread, and Node.js 18.17.0 was released in July 2023. Indirectly, yes, in two situations:
- A
package.jsonpinned during this window. Projects that pinned"next": "13.4.x"in mid-2023 to dodge this problem, then never revisited the pin, still run the four-process baseline (below 13.4.14) or the three-process one (below 13.5.0). Runnpm ls nextand compare. - A base Docker image with an old Node.js. A
node:18-slimimage built before July 2023 predates 18.17.0. If your Dockerfile pins a Node.js major without a minor or patch floor and the image is cached, you can upgrade Next.js past 13.4.13 and still carry theundicileak.
The quickest check for an existing project: confirm that node --version reports 18.17.0 or later and next --version reports 13.5.0 or later. If both hold, the specific problems in this thread do not apply. A memory graph that still climbs after that points elsewhere: Neutkens noted that a data fetching library, a CSS-in-JS solution, a React component or your own code can leak too, and the only way to tell is to compare heap snapshots from the running server.
Further reading: Fix: react-dom/server Import Error in Next.js App Router, useSearchParams Suspense Error: 3 Real Fixes (Next.js 15), use client vs use server in Next.js: The Real Difference.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Continue Reading
Fix: react-dom/server Import Error in Next.js App Router
"You're importing a component that imports react-dom/server" is a compile-time rule of the App Router, not a bug in your code. Here is what the rule protects, which use cases are legitimate, and the fixes verified against Next.js 15.5 and 16.3 with both webpack and Turbopack.
Super expression must either be null or a function: Next.js
Reproduced on Next.js 16.4.0 and 13.5.11: why the carousel import crashes the App Router, the two-file fix that keeps your page a Server Component, and the error that comes next.
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.


