Next.js Memory Leak: Fetch + Mod Standalone — 2 ani fără rezolvare
Next.js aplică un patch peste fetch-ul global și adaugă un strat de cache care pierde memorie la fiecare cerere. În Docker/K8s asta provoacă crash-uri OOM la fiecare câteva ore. Bug-ul există din Next.js 14 și rămâne nerezolvat în 16.2.x.
TL;DR
Next.js aplică un patch peste fetch-ul global și adaugă un strat de cache care păstrează referințe către datele de răspuns după ce ar fi trebuit eliberate. Fiecare apel fetch adaugă memorie care nu este niciodată returnată către GC. În Docker/Kubernetes acest lucru duce la crash-uri OOM la fiecare câteva ore. Bug-ul există din Next.js 14 (aprilie 2024) și rămâne nerezolvat în 16.2.x (martie 2026). Pe Vercel problema nu se manifestă datorită funcțiilor serverless efemere.
Cum funcționează un fetch normal
Request → fetch → got data → response to user → GC cleans up → memory freeCum funcționează fetch în Next.js
Next.js interceptează fetch-ul global și îl împachetează cu propriul strat de cache/tracking:
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 indefinitelyCe se întâmplă în producție
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 accumulatesVersiuni afectate
Next.js — Toate versiunile cu 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
Testat pe Node.js 20, 22, 24, 25 — pierde memorie pe toate.
Ce nu funcționează
| 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) |
Ce funcționează (workaround-uri)
| 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 |
Limitarea workaround-ului cu axios
Propriile apeluri API pot fi înlocuite cu axios. Dar Next.js folosește intern fetch-ul modificat pentru:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Preluarea datelor în Server Components cu deduplicare
use cache(Next.js 16)
Chiar și fără un singur fetch în codul tău — Next.js îl folosește tot intern.
De ce Vercel nu rezolvă problema
Logica de business
Vercel este o companie care câștigă bani din găzduirea Next.js. Pe platforma lor problema nu se manifestă (serverless = efemer). Bug-ul afectează doar self-hosted (Docker, K8s, VPS) — adică pe cei care nu plătesc Vercel.
Poziția oficială
Tim Neutkens (maintainer Vercel) a analizat problema și a declarat că este o problemă de undici (biblioteca fetch din Node.js), nu de Next.js. Issue-ul #90433 a fost închis. În ciuda faptului că:
- axios și node-fetch pe același Node.js funcționează fără scurgeri
- Scurgerea apare doar când fetch trece prin wrapper-ul Next.js
- Bug-ul este deschis de 2 ani fără o rezolvare
Priorități
În acești 2 ani echipa Next.js a livrat:
- Turbopack (build-uri de 2-5x mai rapide) — avantaj de marketing
- Cache Components /
use cache— reduce încărcarea pe serverele Vercel proxy.tsîn locul middleware-ului — simplifică deployment-ul edge pe Vercel- DevTools MCP — hype-ul AI
Memory leak în self-hosted? Nu e o prioritate.
Soluția: AWS Lambda (SST + OpenNext)
Ce este
OpenNext este un adaptor open-source care convertește un build Next.js într-un format pentru AWS Lambda. SST este un framework care automatizează infrastructura.
Arhitectură
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)De ce asta rezolvă memory leak-ul
Funcțiile Lambda procesează cererile și sunt reciclate după 5-15 minute de inactivitate. Memoria nu apucă să se acumuleze.
Deployment
npx sst@latest init
npx sst deploy --stage productionComparație
| 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 |
Nuanțe Lambda
- Cold starts — prima cerere este mai lentă (~200-500ms)
- Securitate — activează OAC (Origin Access Control), altfel URL-ul Lambda este public
- OpenNext — proiect comunitar, nu oficial Vercel. Funcțiile noi din Next.js se pot strica
- Atac asupra portofelului — în timpul unui DDoS, auto-scaling-ul Lambda poate genera o factură mare
De ce o rezolvare reală este nerealistă
1. Problemă arhitecturală
Scurgerea nu este un bug accidental, ci consecința unei decizii de design: Next.js interceptează fetch-ul global și adaugă cache/tracking peste el. Ca să o repari — trebuie regândit modul în care App Router interacționează cu fetch. Asta afectează ISR, revalidation, data cache, request deduplication — nucleul framework-ului.
2. Conflict de interese
Vercel nu este motivat să repare ceva ce nu le afectează platforma. Self-hosted concurează cu business-ul lor. Cu cât sunt mai multe probleme în self-hosted — cu atât mai mulți oameni migrează pe Vercel.
3. Pasarea vinei
Poziția oficială este "e undici, nu noi". Până când asta nu se schimbă — nu vor lucra la o rezolvare.
4. Nicio rezolvare din comunitate
Licența AGPL-3.0 a Next.js permite fork-uri, dar codebase-ul este uriaș și strâns cuplat cu infrastructura Vercel. Un PR din comunitate pentru a repara wrapper-ul fetch ar necesita o înțelegere profundă a arhitecturii interne și aprobarea maintainerilor — care au închis deja issue-ul.
Concluzii
- Dacă ești pe Vercel — nicio problemă, nu ai ce face
- Dacă ești self-hosted și ai nevoie de serverless — SST + OpenNext pe AWS Lambda
- Dacă ești self-hosted Docker — înlocuiește fetch cu axios unde se poate, monitorizează RAM-ul, configurează restart automat al pod-urilor
- Dacă începi un proiect nou — ia în calcul SvelteKit sau Nuxt ca alternative fără această problemă
Surse
- 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