Next.js Memory Leak: Fetch + Modo Standalone — 2 años sin solución
Next.js parchea el fetch global y añade una capa de caché que pierde memoria en cada petición. En Docker/K8s esto causa crashes OOM cada pocas horas. El bug existe desde Next.js 14 y sigue sin resolverse en 16.2.x.
TL;DR
Next.js parchea el fetch global y añade una capa de caché que mantiene referencias a los datos de respuesta después de que deberían haber sido liberados. Cada llamada a fetch añade memoria que nunca es devuelta al GC. En Docker/Kubernetes esto provoca crashes OOM cada pocas horas. El bug existe desde Next.js 14 (abril 2024) y sigue sin resolverse en 16.2.x (marzo 2026). En Vercel el problema no se manifiesta gracias a las funciones serverless efímeras.
Cómo funciona fetch normalmente
Request → fetch → got data → response to user → GC cleans up → memory freeCómo funciona fetch en Next.js
Next.js intercepta el fetch global y lo envuelve con su propia capa de caché/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 indefinitelyQué ocurre en producción
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 accumulatesVersiones afectadas
Next.js — Todas las versiones con 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
Probado en Node.js 20, 22, 24, 25 — fuga en todos.
Qué no funciona
| 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) |
Qué funciona (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 |
Limitación del workaround con axios
Tus propias llamadas API pueden reemplazarse con axios. Pero Next.js usa internamente el fetch parcheado para:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Data fetching de Server Components con deduplicación
use cache(Next.js 16)
Incluso sin un solo fetch en tu código — Next.js lo sigue usando internamente.
Por qué Vercel no lo arregla
Lógica de negocio
Vercel es una empresa que gana dinero alojando Next.js. En su plataforma el problema no se manifiesta (serverless = efímero). El bug solo afecta a self-hosted (Docker, K8s, VPS) — los que no pagan a Vercel.
Posición oficial
Tim Neutkens (mantenedor de Vercel) analizó el problema y declaró que es un problema de undici (biblioteca fetch de Node.js), no de Next.js. El issue #90433 fue cerrado. A pesar de que:
- axios y node-fetch en el mismo Node.js funcionan sin fugas
- La fuga solo aparece cuando fetch pasa por el wrapper de Next.js
- El bug lleva 2 años abierto sin solución
Prioridades
En estos 2 años el equipo de Next.js lanzó:
- Turbopack (builds 2-5x más rápidos) — ventaja de marketing
- Cache Components /
use cache— reduce carga en servidores Vercel proxy.tsen lugar de middleware — simplifica el despliegue edge en Vercel- DevTools MCP — hype de IA
¿Memory leak en self-hosted? No es prioridad.
Solución: AWS Lambda (SST + OpenNext)
Qué es
OpenNext es un adaptador open-source que convierte un build de Next.js en formato para AWS Lambda. SST es un framework que automatiza la infraestructura.
Arquitectura
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)Por qué esto resuelve el memory leak
Las funciones Lambda procesan peticiones y se reciclan tras 5-15 minutos de inactividad. La memoria no tiene tiempo de acumularse.
Despliegue
npx sst@latest init
npx sst deploy --stage productionComparación
| 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 |
Matices de Lambda
- Cold starts — la primera petición es más lenta (~200-500ms)
- Seguridad — activar OAC (Origin Access Control), sino la URL de Lambda es pública
- OpenNext — proyecto comunitario, no oficial de Vercel. Nuevas funciones de Next.js pueden romperse
- Ataque al monedero — durante DDoS, el auto-scaling de Lambda puede generar una factura grande
Por qué una solución real es poco realista
1. Problema arquitectónico
La fuga no es un bug accidental sino consecuencia de una decisión de diseño: Next.js intercepta el fetch global y añade caché/tracking encima. Para arreglarlo hay que rediseñar cómo App Router interactúa con fetch. Esto afecta ISR, revalidation, data cache, request deduplication — el núcleo del framework.
2. Conflicto de intereses
Vercel no está motivado a arreglar lo que no afecta su plataforma. Self-hosted compite con su negocio. Cuantos más problemas en self-hosted — más personas migran a Vercel.
3. Echar la culpa
La posición oficial es "es undici, no nosotros". Hasta que eso cambie — no trabajarán en una solución.
4. Sin solución comunitaria
La licencia AGPL-3.0 de Next.js permite forks, pero la base de código es enorme y está estrechamente acoplada con la infraestructura de Vercel. Un PR comunitario para arreglar el wrapper de fetch requeriría un profundo conocimiento de la arquitectura interna y la aprobación de los mantenedores — que ya cerraron el issue.
Conclusiones
- Si estás en Vercel — no hay problema, nada que hacer
- Si self-hosted y necesitas serverless — SST + OpenNext en AWS Lambda
- Si self-hosted Docker — reemplazar fetch con axios donde sea posible, monitorear RAM, configurar reinicios automáticos de pods
- Si empiezas un proyecto nuevo — considerar SvelteKit o Nuxt como alternativas sin este problema
Fuentes
- 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