Next.js Memory Leak: Fetch + Standalone-modus — 2 år uten fiks
Next.js patcher den globale fetch og legger til et cache-lag som lekker minne ved hver forespørsel. I Docker/K8s fører dette til OOM-krasj hver par timer. Buggen har eksistert siden Next.js 14 og er fortsatt uløst i 16.2.x.
tee() på fetch-responsstrømmen for å cache den, men registrerte bare én av de to grenene for opprydding, så den andre ble aldri frigjort. Begge klonede bodyer går nå gjennom FinalizationRegistry, sammen med en fiks for LRU-eviction med størrelse 0 (#89040). Merget i canary tidlig i 2026, backportet til stable og med i Next.js 16.3. Hvis du self-hoster med output: standalone, oppgrader til 16.3+ og test på nytt før du fjerner løsningene nedenfor. (Andre lekkasjer — RSC-rendertre-retensjon, Bun — var separate oppfølginger.)TL;DR
Next.js patcher den globale fetch og legger til et cache-lag som holder referanser til responsdata etter at de burde ha blitt frigjort. Hvert fetch-kall legger til minne som aldri returneres til GC. I Docker/Kubernetes fører dette til OOM-krasj hver par timer. Buggen har eksistert siden Next.js 14 (april 2024) og er fortsatt uløst i 16.2.x (mars 2026). På Vercel viser ikke problemet seg takket være efemere serverless-funksjoner.
Hvordan normal fetch fungerer
Request → fetch → got data → response to user → GC cleans up → memory freeHvordan fetch fungerer i Next.js
Next.js fanger opp den globale fetch og pakker den inn med sitt 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 indefinitelyHva som skjer i produksjon
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 versjoner
Next.js — Alle versjoner 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 — lekker på alle.
Hva som ikke fungerer
| 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) |
Hva som fungerer (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 |
Begrensning med axios-workaround
Dine egne API-kall kan erstattes med axios. Men Next.js bruker internt den patchede fetch for:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Server Components data fetching med deduplisering
use cache(Next.js 16)
Selv uten en eneste fetch i koden din — bruker Next.js den fortsatt internt.
Hvorfor Vercel ikke fikser det
Forretningslogikk
Vercel er et selskap som tjener penger på å hoste Next.js. På deres plattform viser ikke problemet seg (serverless = efemert). Buggen påvirker bare self-hosted (Docker, K8s, VPS) — de som ikke betaler Vercel.
Offisiell posisjon
Tim Neutkens (Vercel-maintainer) analyserte problemet og erklærte at det er et undici-problem (Node.js fetch-bibliotek), ikke Next.js. Issue #90433 ble stengt. Til tross for at:
- axios og node-fetch på samme Node.js fungerer uten lekkasjer
- Lekkasjen bare oppstår når fetch går gjennom Next.js-wrapperen
- Buggen har vært åpen i 2 år uten fiks
Prioriteringer
I løpet av disse 2 årene har Next.js-teamet sluppet:
- Turbopack (2-5x raskere builds) — markedsføringsfordel
- Cache Components /
use cache— reduserer 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)
Hva er det
OpenNext er en open-source-adapter som konverterer en Next.js-build til et format for AWS Lambda. SST er et rammeverk som 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 minneslekkasjen
Lambda-funksjoner behandler forespørsler og resirkuleres etter 5-15 minutters inaktivitet. Minnet rekker ikke å akkumuleres.
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-nyanser
- Cold starts — første forespørsel er tregere (~200-500ms)
- Sikkerhet — aktiver OAC (Origin Access Control), ellers er Lambda-URL-en offentlig
- OpenNext — community-prosjekt, ikke offisielt fra Vercel. Nye Next.js-funksjoner kan gå i stykker
- Lommebokattack — ved DDoS kan Lambda auto-scaling føre til en stor regning
Hvorfor en ekte fiks er urealistisk
1. Arkitekturproblem
Lekkasjen er ikke en tilfeldig bug, men en konsekvens av en designbeslutning: Next.js fanger opp den globale fetch og legger til cache/tracking oppå. For å fikse det må måten App Router interagerer med fetch redesignes. Dette påvirker ISR, revalidation, data cache, request deduplication — kjernen i rammeverket.
2. Interessekonflikt
Vercel er ikke motivert til å fikse det som ikke påvirker deres plattform. Self-hosted konkurrerer med deres forretning. Jo flere problemer i self-hosted — jo flere migrerer til Vercel.
3. Skyldforskyvning
Den offisielle posisjonen er "det er undici, ikke oss". Inntil det endrer seg — vil de ikke jobbe med en fiks.
4. Ingen community-fiks
Next.js AGPL-3.0-lisens tillater forks, men kodebasen er enorm og tett koblet til Vercel-infrastrukturen. En community-PR for å fikse fetch-wrapperen ville kreve dyp forståelse av den interne arkitekturen og godkjenning fra maintainers — som allerede har stengt issuen.
Konklusjoner
- Hvis du er på Vercel — ikke noe problem, ingenting å gjøre
- Hvis self-hosted og trenger serverless — SST + OpenNext på AWS Lambda
- Hvis self-hosted Docker — erstatt fetch med axios der det er mulig, overvåk RAM, konfigurer automatisk pod-omstart
- Hvis du starter et nytt prosjekt — vurder SvelteKit eller Nuxt som alternativer uten dette problemet
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
Fant du en feil?
Et feil faktum, en skjev oversettelse, noe som virker usant i denne artikkelen? Skriv til meg — på ditt eget språk.