Next.js Memory Leak: Fetch + Standalone-tilstand — 2 år uden fix
Next.js patcher den globale fetch og tilføjer et cache-lag, der lækker hukommelse ved hver anmodning. I Docker/K8s fører dette til OOM-nedbrud hver par timer. Buggen har eksisteret siden Next.js 14 og er stadig uløst i 16.2.x.
tee() på fetch-svarets stream for at cache den, men registrerede kun den ene af de to grene til oprydning, så den anden aldrig blev frigivet. Begge klonede bodies går nu gennem FinalizationRegistry, sammen med en rettelse af LRU-eviction med størrelse 0 (#89040). Merget i canary tidligt i 2026, backportet til stable og med i Next.js 16.3. Hvis du selv-hoster med output: standalone, så opgradér til 16.3+ og test igen, før du fjerner løsningerne nedenfor. (Andre læk — RSC-rendertræ-retention, Bun — var separate opfølgninger.)TL;DR
Next.js patcher den globale fetch og tilføjer et cache-lag, der holder referencer til responsdata, efter de burde have været frigivet. Hvert fetch-kald tilføjer hukommelse, der aldrig returneres til GC. I Docker/Kubernetes fører dette til OOM-nedbrud hver par timer. Buggen har eksisteret siden Next.js 14 (april 2024) og er stadig uløst i 16.2.x (marts 2026). På Vercel viser problemet sig ikke takket være efemere serverless-funktioner.
Hvordan normal fetch fungerer
Request → fetch → got data → response to user → GC cleans up → memory freeHvordan fetch fungerer i Next.js
Next.js opfanger den globale fetch og pakker den ind med sit eget cache-/tracking-lag:
Request → Next.js fetch wrapper → creates:
- Performance entry (speed tracking) ← not cleaned
- Request metadata object ← not cleaned
- Internal promise wrapper ← not cleaned
- Cache lookup entry ← not cleaned
- Response body (for unique URL) ← not cleaned
→ Response to user
→ GC sees objects are still referenced → doesn't touch them
→ Memory grows indefinitelyHvad der sker i produktion
Self-hosted (Docker/K8s):
Request 1: fetch → +100KB → RAM: 100KB
Request 2: fetch → +100KB → RAM: 200KB
Request 1000: fetch → +100KB → RAM: 100MB
Request 50,000: fetch → +100KB → RAM: 5GB → OOM Kill 💀
→ Kubernetes restarts the pod → cycle repeats
Vercel (Serverless):
Request 1: [start] → fetch → +100KB → [process dies] → 0 KB ✅
Request 2: [start] → fetch → +100KB → [process dies] → 0 KB ✅
→ Memory never accumulatesBerørte versioner
Next.js — Alle versioner med App Router
| Version | Issue | Date |
|---|---|---|
| 14.2.x | #64212 | April 2024 |
| 14.x-15.x | #68578 | August 2024 |
| 14.3.0-canary | #79588 | May 2025 |
| 16.0.1 | #85914 | November 2025 |
| 16.1.0 | #88603 | January 2026 |
| 16.0.10 | #90433 | February 2026 |
| 16.2.0-canary.51 | Confirmed in #90433 comments | March 2026 |
Node.js
Testet på Node.js 20, 22, 24, 25 — lækker på alle.
Hvad der ikke virker
| Attempt | Result |
|---|---|
cacheMaxMemorySize: 0 | Doesn't help — leak is not from this cache |
| Disable image optimization | Doesn't help |
--max-old-space-size=6144 | Just crashes slower |
Read response via .json() / .text() | Doesn't help |
| Remove React fetch patching (canary.45) | Doesn't help |
| Upgrade to latest version | Doesn't help (leaks on 16.2.0-canary.51 too) |
Hvad der virker (workarounds)
| Workaround | Why it helps |
|---|---|
Replace fetch with axios | Bypasses Next.js wrapper |
Replace fetch with node-fetch | Same reason |
| Downgrade Node.js to 20.15.1 | Older undici has fewer leaks (for some) |
Docker image node:20-alpine3.21 | Helps in some cases |
| Deploy to Lambda (SST + OpenNext) | Ephemeral functions — memory doesn't accumulate |
| Deploy to Vercel | Same — serverless |
Begrænsning med axios-workaround
Dine egne API-kald kan erstattes med axios. Men Next.js bruger internt den patchede fetch til:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Server Components data fetching med deduplikering
use cache(Next.js 16)
Selv uden en eneste fetch i din kode — bruger Next.js den stadig internt.
Hvorfor Vercel ikke fikser det
Forretningslogik
Vercel er en virksomhed, der tjener penge på at hoste Next.js. På deres platform viser problemet sig ikke (serverless = efemert). Buggen påvirker kun self-hosted (Docker, K8s, VPS) — dem, der ikke betaler Vercel.
Officiel position
Tim Neutkens (Vercel-maintainer) analyserede problemet og erklærede det for et undici-problem (Node.js fetch-bibliotek), ikke Next.js. Issue #90433 blev lukket. På trods af at:
- axios og node-fetch på samme Node.js fungerer uden lækager
- Lækagen kun opstår, når fetch går gennem Next.js-wrapperen
- Buggen har været åben i 2 år uden en fix
Prioriteringer
I løbet af disse 2 år har Next.js-teamet udgivet:
- Turbopack (2-5x hurtigere builds) — markedsføringsfordel
- Cache Components /
use cache— reducerer belastningen på Vercel-servere proxy.tsi stedet for middleware — forenkler edge-deployment på Vercel- DevTools MCP — AI-hype
Memory leak i self-hosted? Ikke en prioritet.
Løsning: AWS Lambda (SST + OpenNext)
Hvad er det
OpenNext er en open-source-adapter, der konverterer et Next.js-build til et format til AWS Lambda. SST er et framework, der automatiserer infrastrukturen.
Arkitektur
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)Hvorfor dette løser hukommelseslækagen
Lambda-funktioner behandler anmodninger og genbruges efter 5-15 minutters inaktivitet. Hukommelsen når ikke at akkumulere.
Deployment
npx sst@latest init
npx sst deploy --stage productionSammenligning
| Vercel | AWS Lambda (SST) | Docker self-hosted | |
|---|---|---|---|
| Memory leak | Not felt | Not felt | Critical |
| Cold starts | Yes | Yes (~200-500ms) | No |
| Price (~medium traffic) | $20-150/mo | $5-30/mo | $5-50/mo |
| Control | Minimal | Full | Full |
| ISR/Revalidation | Works | Works (SQS) | Works (with leak) |
| Vendor lock-in | Vercel | AWS | None |
Lambda-nuancer
- Cold starts — første anmodning er langsommere (~200-500ms)
- Sikkerhed — aktiver OAC (Origin Access Control), ellers er Lambda-URL'en offentlig
- OpenNext — community-projekt, ikke officielt fra Vercel. Nye Next.js-funktioner kan gå i stykker
- Tegnebogsangreb — ved DDoS kan Lambda auto-scaling føre til en stor regning
Hvorfor en rigtig fix er urealistisk
1. Arkitekturproblem
Lækagen er ikke en tilfældig bug, men en konsekvens af en designbeslutning: Next.js opfanger den globale fetch og tilføjer cache/tracking ovenpå. For at fixe det skal måden, App Router interagerer med fetch, redesignes. Dette påvirker ISR, revalidation, data cache, request deduplication — frameworkets kerne.
2. Interessekonflikt
Vercel er ikke motiveret til at fixe det, der ikke påvirker deres platform. Self-hosted konkurrerer med deres forretning. Jo flere problemer i self-hosted — jo flere migrerer til Vercel.
3. Skyldforskydning
Den officielle position er "det er undici, ikke os". Indtil det ændrer sig — vil de ikke arbejde på en fix.
4. Ingen community-fix
Next.js AGPL-3.0-licens tillader forks, men kodebasen er enorm og tæt koblet til Vercel-infrastrukturen. En community-PR til at fixe fetch-wrapperen ville kræve dyb forståelse af den interne arkitektur og godkendelse fra maintainers — som allerede har lukket issuen.
Konklusioner
- Hvis du er på Vercel — intet problem, intet at gøre
- Hvis self-hosted og behov for serverless — SST + OpenNext på AWS Lambda
- Hvis self-hosted Docker — erstat fetch med axios hvor muligt, overvåg RAM, konfigurer automatisk pod-genstart
- Hvis du starter et nyt projekt — overvej SvelteKit eller Nuxt som alternativer uden dette problem
Kilder
- Issue #64212 — Memory Leak with global fetch (April 2024)
- Issue #68578 — Possible memory leak in Fetch API (August 2024)
- Issue #85914 — Memory Leak with fetch + standalone (November 2025)
- Issue #90433 — OOM in 16.0.10 (February 2026)
- Discussion #88603 — OOM in Docker/K8s (January 2026)
- Discussion #88078 — cacheMaxMemorySize behavior
- OpenNext
- SST — Next.js on AWS
- Secret knowledge to self-host Next.js
- Next.js Memory Usage Guide
Fandt du en fejl?
Et forkert faktum, en skæv oversættelse, noget der virker usandt i denne artikel? Skriv til mig — på dit eget sprog.