Next.js Memory Leak: Fetch + Standalone Mode — 2 роки без фіксу
Next.js патчить глобальний fetch та додає кеш-шар, який тече на кожному запиті. В Docker/K8s це призводить до OOM crash кожні кілька годин. Баг існує з Next.js 14 і досі не вирішений у 16.2.x.
TL;DR
Next.js патчить глобальний fetch та додає кеш-шар, який тримає references на response data після того як вони мали б бути звільнені. Кожен fetch виклик додає пам'яті яка ніколи не повертається GC. В Docker/Kubernetes це призводить до OOM crash кожні кілька годин. Баг існує з Next.js 14 (квітень 2024) і досі не вирішений у 16.2.x (березень 2026). На Vercel проблема не проявляється через ephemeral serverless функції.
Як працює нормальний fetch
Request → fetch → got data → response to user → GC cleans up → memory freeЯк працює fetch у Next.js
Next.js перехоплює глобальний fetch і обгортає його своїм кеш/трекінг шаром:
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Що відбувається на продакшні
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Уражені версії
Next.js — всі версії з 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
Тестували на Node.js 20, 22, 24, 25 — на всіх тече.
Що не працює
| 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) |
Що працює (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 |
Обмеження workaround з axios
Свої API-запити можна замінити на axios. Але Next.js внутрішньо використовує патчений fetch для:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Server Components data fetching з дедуплікацією
use cache(Next.js 16)
Тобто навіть без жодного fetch у своєму коді — Next.js все одно використовує його під капотом.
Чому Vercel не фіксить
Бізнес-логіка
Vercel — компанія яка заробляє на хостингу Next.js. На їхній платформі проблема не проявляється (serverless = ephemeral). Баг зачіпає тільки self-hosted (Docker, K8s, VPS) — тобто тих хто не платить Vercel.
Офіційна позиція
Tim Neutkens (мейнтейнер Vercel) провів аналіз і заявив що це проблема undici (Node.js fetch бібліотека), а не Next.js. Issue #90433 закрили. При тому що:
- axios і node-fetch на тому ж Node.js працюють без витоків
- Витік з'являється тільки коли fetch проходить через Next.js обгортку
- Баг відкритий 2 роки без фіксу
Пріоритети
За ці 2 роки команда Next.js випустила:
- Turbopack (2-5x швидші білди) — маркетингова перевага
- Cache Components /
use cache— зменшує навантаження на Vercel сервери proxy.tsзамість middleware — спрощує edge-деплой на Vercel- DevTools MCP — AI-хайп
Memory leak у self-hosted? Не в пріоритетах.
Рішення: AWS Lambda (SST + OpenNext)
Що це
OpenNext — open-source адаптер який перетворює Next.js build у формат для AWS Lambda. SST — фреймворк який автоматизує інфраструктуру.
Архітектура
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)Чому це вирішує memory leak
Lambda функція обробляє запити і recycled через 5-15 хвилин неактивності. Пам'ять не встигає накопитись.
Деплой
npx sst@latest init
npx sst deploy --stage productionПорівняння
| 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
- Cold starts — перший запит повільніший (~200-500ms)
- Безпека — увімкнути OAC (Origin Access Control), інакше Lambda URL публічний
- OpenNext — community проєкт, не офіційний Vercel. Нові фічі Next.js можуть ламатись
- Wallet attack — при DDoS автоскейлінг Lambda може призвести до великого рахунку
Чому вирішити нереально
1. Архітектурна проблема
Витік — не випадковий баг, а наслідок дизайн-рішення: Next.js перехоплює глобальний fetch і додає кеш/трекінг поверх. Щоб пофіксити — потрібно переробити як App Router взаємодіє з fetch. Це зачіпає ISR, revalidation, data cache, request deduplication — ядро фреймворку.
2. Конфлікт інтересів
Vercel не мотивований фіксити те що не зачіпає їхню платформу. Self-hosted конкурує з їхнім бізнесом. Чим більше проблем у self-hosted — тим більше людей мігрують на Vercel.
3. Blame shifting
Офіційна позиція — "це undici, не ми". Поки вона не зміниться — над фіксом не працюватимуть.
4. Немає community fix
AGPL-3.0 ліцензія Next.js дозволяє форки, але кодова база величезна і тісно зв'язана з Vercel інфраструктурою. Community PR з фіксом fetch обгортки потребував би глибокого розуміння внутрішньої архітектури і схвалення мейнтейнерів — які вже закрили issue.
Висновки
- Якщо на Vercel — проблеми немає, не потрібно нічого робити
- Якщо self-hosted і потрібен serverless — SST + OpenNext на AWS Lambda
- Якщо self-hosted Docker — замінити fetch на axios де можливо, моніторити RAM, налаштувати автоматичний рестарт подів
- Якщо починаєш новий проєкт — розглянути SvelteKit або Nuxt як альтернативу без цієї проблеми
Джерела
- 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