vercel / vercel/next.js

next dev overrides container-aware V8 heap limit using host memory

Open
#98,381 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Performance Runtime
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.