Skip to main content
Tilbage til bloggen
Next.jsMemory LeakDockerAWS LambdaNode.js

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.

Udgivet 19. marts 2026Opdateret 10. august 202610 min læsning
Opdatering (august 2026): Det kunne faktisk løses — konklusionen nedenfor om, at en rettelse var "urealistisk", var forkert. Årsagen blev fundet i PR #88577: Next.js laver 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 free

Hvordan 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 indefinitely

Hvad 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 accumulates

Berørte versioner

Next.js — Alle versioner med App Router

VersionIssueDate
14.2.x#64212April 2024
14.x-15.x#68578August 2024
14.3.0-canary#79588May 2025
16.0.1#85914November 2025
16.1.0#88603January 2026
16.0.10#90433February 2026
16.2.0-canary.51Confirmed in #90433 commentsMarch 2026

Node.js

Testet på Node.js 20, 22, 24, 25 — lækker på alle.

Hvad der ikke virker

AttemptResult
cacheMaxMemorySize: 0Doesn't help — leak is not from this cache
Disable image optimizationDoesn't help
--max-old-space-size=6144Just crashes slower
Read response via .json() / .text()Doesn't help
Remove React fetch patching (canary.45)Doesn't help
Upgrade to latest versionDoesn't help (leaks on 16.2.0-canary.51 too)

Hvad der virker (workarounds)

WorkaroundWhy it helps
Replace fetch with axiosBypasses Next.js wrapper
Replace fetch with node-fetchSame reason
Downgrade Node.js to 20.15.1Older undici has fewer leaks (for some)
Docker image node:20-alpine3.21Helps in some cases
Deploy to Lambda (SST + OpenNext)Ephemeral functions — memory doesn't accumulate
Deploy to VercelSame — 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.ts i 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 production

Sammenligning

VercelAWS Lambda (SST)Docker self-hosted
Memory leakNot feltNot feltCritical
Cold startsYesYes (~200-500ms)No
Price (~medium traffic)$20-150/mo$5-30/mo$5-50/mo
ControlMinimalFullFull
ISR/RevalidationWorksWorks (SQS)Works (with leak)
Vendor lock-inVercelAWSNone

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

  1. Hvis du er på Vercel — intet problem, intet at gøre
  2. Hvis self-hosted og behov for serverless — SST + OpenNext på AWS Lambda
  3. Hvis self-hosted Docker — erstat fetch med axios hvor muligt, overvåg RAM, konfigurer automatisk pod-genstart
  4. Hvis du starter et nyt projekt — overvej SvelteKit eller Nuxt som alternativer uden dette problem

Kilder

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.