Issue #49929 on vercel/next.js opened on 2023-05-17 with a small reproduction: a Next.js 13.4.3-canary App Router project that behaved normally on Vercel and climbed to 220MB of steady memory use on Fly.io. It closed 2023-08-24 with 131 comments, screenshots from Kubernetes pods, Railway deployments, and Docker containers all showing the same shape — memory climbing until the process was killed and restarted — and two Vercel engineers, Tim Neutkens and Lee Robinson, running the investigation live in the thread. 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
Every report in the thread described the same pattern: a Next.js app using the App Router (introduced as the default in 13.4) would start at a normal baseline, then grow memory usage with every request or page navigation, and never release it — until the host's memory limit was hit and the process was killed. On free-tier hosts with 256MB of RAM (Fly.io's free allowance was cited repeatedly), this happened within hours. On larger Kubernetes pods with 2-4GB, it took days, but the trend line was the same: up, never down.
The scale of the reports made this hard to dismiss as one team's misconfiguration: karl-run measured 10x the idle memory of an equivalent Pages Router app on the same Kubernetes cluster; stx-chris watched a container "accumulate objects but never release them" until it hit its 2GB limit 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 a memory leak in a framework used by millions of deployments is genuinely hard to diagnose — not because the team was slow, but because of what memory metrics actually measure:
"We used valgrind and lldb and found that the increase in memory usage is based on what the max-old-space-size and other memory options are set on Node.js. [...] there is about 50% of empty memory assigned regardless of it being used, we're assuming this is related to V8 pre-allocating a certain amount of memory to cover increases in heap usage."
In other words, a rising Resident Set Size (RSS) graph — which is what every screenshot in the thread showed — does not by itself prove a leak. V8 retains memory after a load spike instead of returning it to the OS immediately, which looks identical to a leak on a monitoring dashboard. This is the core reason the investigation dragged: most reports were RSS screenshots from production dashboards, and the team needed heap snapshots and a runnable reproduction to tell an actual leak apart from expected V8 behavior. timneutkens said as much directly, repeatedly asking commenters for a reproduction rather than another chart, and getting mostly charts back.
The two causes that were actually confirmed
Buried in the noise, two concrete, fixed causes emerged — and they are unrelated to each other, which is why no single patch "fixed the memory leak":
1. A real Node.js leak in undici's fetch(). Next.js calls fetch() internally with an AbortController signal. Below Node.js 18.17.0, this leaked memory on every call — a bug in Node's bundled undici (its fetch implementation), fixed in undici PR #2049, landing in Node.js 18.17.0. broksonic21's minimal reproduction confirmed this exactly: on Node 18.17.0 with the corresponding Next.js canary, the leak was gone; below that Node version, it wasn't.
2. The App Router's process-per-request-type overhead. Separately from the leak, App Router deployments started with more baseline processes than the Pages Router (one each for routing, App Router rendering, and Pages Router rendering — even when a project used only one router). Neutkens landed PR #53523 to remove one process, then PR #54143 to bring the count down to two, directly reducing the memory floor every App Router deployment started from — independent of whether the undici leak was present.
A third, popular workaround in the thread — uninstalling sharp or setting images: { unoptimized: true } — correlated with lower memory for several commenters, but the team never confirmed it as the same bug. Instead they opened a dedicated issue, #54482, to investigate Image Optimization's memory profile on its own. Conflating that workaround with "the fix" for this issue would be the wrong takeaway — it addressed a different, still-open question.
What actually shipped, and when
-
Node.js ≥ 18.17.0 — required, for the underlying
undici/fetch()leak fix. Projects on older Node 18.x will still see it regardless of Next.js version. -
Next.js 13.4.13-canary.12+ — fixed a related "zombie process" bug where
next startkept running afterCtrl+C. - Next.js post-13.4.20 canaries (PRs #53523, #54143) — reduced the App Router's baseline process count, lowering the memory floor.
If your project is still pinned below 13.4.10 for this exact reason, both the Node.js upgrade and a Next.js upgrade past this window are required together — one without the other only removes part of the symptom.
Is this still relevant if you're starting a project today?
Directly, no — a project scaffolded with a current Next.js version already ships past every fix in this thread, and Node.js 18.17.0 is over three years old at this point. Indirectly, yes, in three situations that still show up in support threads:
-
A
package.jsonpinned during this window. Projects that pinned"next": "13.4.x"in mid-2023 to dodge this exact bug, then never revisited the pin, are still running code from before both fixes landed.npm ls nextand compare against13.4.20as the floor. -
A base Docker image with an old Node.js.
node:18-slimfrom before mid-2023 predates 18.17.0. If your Dockerfile pins a Node major without a minor/patch floor, you can rebuild on a fixed Next.js version and still carry the Node-side leak. -
Reading old Stack Overflow answers as current advice. Several highly-upvoted answers from 2023 recommend
appDir: falseor uninstallingsharpas the fix. Per this thread,appDir: falsewas a workaround for the process-count overhead specifically (moot after PR #54143 shipped), and thesharpcorrelation was never confirmed as the same root cause — treating either as a permanent fix for a 2026 App Router project misattributes the real cause.
The fastest check for an existing project: confirm node --version reports 18.17.0 or later, and next --version reports past 13.4.20 — if both hold, this specific thread does not apply to you, regardless of what a memory graph looks like.
Related Incidents
- Next.js "Server Is Running Out of Memory" Fix (2026) — the dev-mode counterpart of this same era, explicitly split into its own issue by the Next.js team mid-thread.
- Fix 'Next.js 15 requires Node.js 18.18' Build Error — why the Node.js version floor keeps moving, and what breaks when you fall behind it.
-
I Tanked My Core Web Vitals Score With Next.js Images — the Image Optimization /
sharppipeline this thread's workaround touches, covered from the performance side. - Deploy Next.js 15 to Vercel Without Environment Variable Errors — the same App Router deployment surface, from the config side rather than the memory side.
Originally published at https://www.iloveblogs.blog
Top comments (0)