next dev overrides container-aware V8 heap limit using host memory
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/yeager-dev/nextjs-dev-container-heap-limit
To Reproduce
The following reproduction is fully self-contained and does not require an existing project or node_modules on the host.
First, the behavior can be observed directly in Node:
docker run --rm -m 2g node:22 node -e "
const os = require('os'), v8 = require('v8');
console.log('os.totalmem ', Math.floor(os.totalmem()/1048576), 'MB');
console.log('process.constrainedMemory', Math.floor(process.constrainedMemory()/1048576), 'MB');
console.log('node default heap limit ', Math.floor(v8.getHeapStatistics().heap_size_limit/1048576), 'MB');
console.log('what next dev configures ', Math.floor(Math.floor(os.totalmem()/1048576)*0.5), 'MB');
"
On a 32 GB host with a 2 GB container limit, this produces approximately:
os.totalmem 31781 MB
process.constrainedMemory 2048 MB
node default heap limit 1048 MB
what next dev configures 15890 MB
The actual Next.js behavior can be reproduced with:
docker run --rm -m 2g node:22 sh -c '
cd /tmp
npx --yes create-next-app@latest app --ts --app --no-eslint --no-tailwind \
--no-src-dir --import-alias "@/*" --use-npm > /dev/null 2>&1
cd /tmp/app
npx next dev > /tmp/dev.log 2>&1 &
sleep 25
head -3 /tmp/dev.log
for p in /proc/[0-9]*; do
tr "\0" "\n" < $p/environ 2>/dev/null | grep "^NODE_OPTIONS=.*max-old-space-size"
done
'
On Next.js 16.3.4, this produces:
▲ Next.js 16.3.4 (Turbopack)
- Local: http://localhost:3000
✓ Ready in 252ms
NODE_OPTIONS=--max-old-space-size=15890 --enable-source-maps
Current vs. Expected behavior
Current behavior :
next dev uses approximately 50% of os.totalmem() to configure --max-old-space-size. On a 32 GB host with a 2 GB container limit, this results in a heap limit of approximately 15.9 GB, even though the process can only use 2 GB.
Node already detects the container constraint through process.constrainedMemory() and selects a compatible heap limit when no override is applied. The problem is that Next.js subsequently replaces this runtime-aware decision with a host-memory-based heuristic.
Expected behavior :
next dev should respect the memory limit imposed by the container/cgroup when calculating the V8 heap size.
Provide environment information
The issue reproduces inside a `docker run -m 2g node:22` container on a 32 GB host, using Node 22.23.2 and Next.js 16.3.4. It was also reproduced on `16.4.0-canary.21`.
The heuristic has remained unchanged since PR #57163 in October 2023, so earlier versions are expected to exhibit the same behavior.
Which area(s) are affected? (Select all that apply)
Performance, Runtime
Which stage(s) are affected? (Select all that apply)
next dev (local)
Additional context
The relevant heuristic in next-dev.ts derives the heap-size override from approximately os.totalmem() * 0.5. Current workarounds are NEXT_DISABLE_MEM_OVERRIDE=1 or manually setting NODE_OPTIONS=--max-old-space-size=..., but both require users to know that Next.js is overriding Node's default configuration.
A related issue, #95745, appears to involve the same broader class of memory-limit detection problems in next build workers. The key distinction here is that Node already understands the container constraint; next dev subsequently replaces that correct, runtime-aware decision with a host-memory-based value, which can cause devMemoryThresholdRestart to lose its intended effectiveness.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with next-dev.ts and trace the heap-size override derived from os.totalmem(). Run the provided docker run -m 2g node:22 reproduction and compare NODE_OPTIONS with process.constrainedMemory() and Node’s default heap limit. Done when next dev no longer selects a host-memory-based value under the container limit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nextjs, node.js
- Domain
- devops, performance
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100