Short answer
For a one-off build, raise the limit with NODE_OPTIONS=--max-old-space-size=4096 (in MiB), and keep it below the machine’s or container’s memory. In CI and Docker, check the runner really has that much RAM. For a server that grows until it crashes, raising the limit only delays the crash: take heap snapshots and look for caches, arrays or listeners that only grow. Also stop loading whole files or query results into memory at once: stream or page them.
Why the heap ran out, and the fix
01
The build needs more memory than the default
“Sets the max memory size of V8's old memory section.” Node.js, Command-line options: --max-old-space-size
Big TypeScript projects, source maps and bundlers can need more heap than Node.js picks by default. Raise the old-space limit for that command, set in MiB.
# one command NODE_OPTIONS=--max-old-space-size=4096 npm run build # package.json (use cross-env on Windows) "build": "NODE_OPTIONS=--max-old-space-size=4096 next build"02
The limit is higher than the machine has
“On a machine with 2 GiB of memory, consider setting this to 1536 (1.5 GiB) to leave some memory for other uses and avoid swapping.” Node.js, Command-line options: --max-old-space-size
Setting 8192 on a 4 GB CI runner or container swaps or gets killed by the system (exit code 137) instead. Leave room for the operating system and other processes, and pick a larger runner if the build really needs more.
03
A memory leak in a long-running server
If memory climbs steadily until the process dies, something keeps references: an in-memory cache without a limit, a growing array or Map, event listeners added per request, or timers never cleared. Take two heap snapshots a few minutes apart and compare what grew. Take them well before the limit, because a snapshot of a nearly full heap can run out of memory itself.
node --inspect=127.0.0.1:9229 server.js # never expose the inspector publicly # open chrome://inspect, Memory tab, take a heap snapshot, # send traffic, take another, select it and switch the view to Comparison04
Loading everything at once
Reading a large file with readFileSync, building one huge array from a database query, or JSON.stringify on a large object all need the whole thing in memory. Stream files and page through queries.
import { createReadStream } from 'node:fs'; import readline from 'node:readline'; const lines = readline.createInterface({ input: createReadStream('big.csv') }); for await (const line of lines) handle(line);
Checked against the vendor’s docs on : Node.js: Command-line options (--max-old-space-size).
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
Related: Firebase Functions memory limit exceeded · Node.js ECONNRESET (“socket hang up”) · Node.js “Cannot find module”