EthioDeploy
DocsPricingDomainsBlogToolsChangelog

Blog/Fix deploy errors

JavaScript heap out of memory during next build

BS
Brook SolomonPublished November 3, 2026
Share on XShare on LinkedIn
heap limit

THE SHORT ANSWER

next build fails with "JavaScript heap out of memory" when the Node.js process running the build fills V8's old-space heap. The heap has a size limit of its own, separate from how much RAM the machine has. The quick fix is to raise that limit for the build only, with --max-old-space-size set below the memory the build machine actually has. The lasting fix is to find which build step uses the memory (bundling, TypeScript or static page generation) and turn down that step: source maps, the type check, or webpack's cache.

What does JavaScript heap out of memory mean?

Node.js keeps JavaScript objects in V8's heap, and the long-lived part of it, old space, has a maximum size. As memory use nears that limit, V8 spends more and more time collecting garbage. When a collection can't free enough, V8 stops the process and prints one of these:

<--- Last few GCs --->
...
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

# or, from older Node versions
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

The limit is V8's, not the machine's. A laptop with 16 GB of RAM can still hit it, because Node picks its default heap size from the memory it detects and doesn't grow past it. A build that worked last month fails today because the app grew: more pages, more dependencies, a bigger type graph.

Is it the heap or the whole machine?

Two different failures look alike in a build log. Tell them apart before you fix anything:

What the log showsWhat ran outWhat helps
FATAL ERROR: ... JavaScript heap out of memoryNode's heap limit. The machine may still have free memoryA bigger heap limit, or less memory used by the build
Killed, exit code 137, or the build stops with no error at allThe container or machine. The kernel's OOM killer sent SIGKILLLess memory used by the build, or a machine with more. A bigger heap limit makes this worse

The second case is why the usual advice, --max-old-space-size=8192, sometimes “fixes” the heap error and replaces it with exit code 137. A heap limit above the memory the build really has lets V8 keep allocating until the kernel kills it.

How do I raise the heap limit for next build?

Pass --max-old-space-size, in MiB, to the Node process that runs the build. Put it in the build script, so it applies to the build and not to the server you run afterwards:

// package.json
"scripts": {
  "build": "NODE_OPTIONS=--max-old-space-size=3072 next build",
  "start": "next start"
}

// The same without an environment variable (works in any shell)
"build": "node --max-old-space-size=3072 node_modules/next/dist/bin/next build"

The first form needs a Unix shell; on Windows use cross-env or the second form. Pick a number below the memory the build machine has, leaving room for everything outside the heap. Node's own docs suggest 1536 on a machine with 2 GiB. Rough guide:

Memory on the build machine--max-old-space-size
2 GiB1536
4 GiB3072
8 GiB6144

Setting NODE_OPTIONS as an environment variable on your host also works, but it reaches every Node process, including the production server. A 3 GB heap limit on a server container with 512 MB of memory turns a slow garbage collection into an OOM kill.

Which build step runs out of memory?

Read the last line next build printed before the crash. Each step has its own fix, all from the Next.js memory usage guide:

Last step in the logWhat uses the memorySetting to try
Creating an optimized production buildBundling, and source maps for the browser and serverproductionBrowserSourceMaps: false, experimental.serverSourceMaps: false; webpack builds: see below
Running TypeScriptThe type check of the whole projectRun tsc --noEmit in CI and set typescript.ignoreBuildErrors: true
Generating static pagesPrerendering every static page; with cacheComponents, source maps for the prerenderenablePrerenderSourceMaps: false, or fewer pages prerendered at build time

Skipping the type check is safe only if something else runs it. The Next.js docs warn that it “may produce faulty deploys due to type errors”, so add tsc --noEmit to CI or a pre-push hook before you turn it off.

// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  productionBrowserSourceMaps: false,
  experimental: {
    serverSourceMaps: false,
  },
  typescript: {
    // only with tsc --noEmit running in CI
    ignoreBuildErrors: true,
  },
}

export default config

Building with webpack?

Next.js 16 builds with Turbopack by default. If your build runs next build --webpack, or you are on Next.js 15 or older, three webpack settings cut memory use:

  • experimental.webpackMemoryOptimizations: true (Next.js 15 and later) lowers peak memory at the cost of slightly slower compiles. Next.js calls it low-risk.
  • experimental.webpackBuildWorker: true runs compilation in a separate worker. It is on by default since 14.1 unless you have a custom webpack config, so check this one if you do.
  • Webpack's persistent cache holds modules in memory. The Next.js guide shows how to switch it to the memory-only cache in production builds:
webpack: (config, { dev }) => {
  if (config.cache && !dev) {
    config.cache = Object.freeze({ type: 'memory' })
  }
  return config
},

How do I see what is using the memory?

If the limit keeps climbing, measure before raising it again:

# Print heap use and GC stats through the build; snapshots near the limit
next build --experimental-debug-memory-usage

# Write a .heapprofile to load in Chrome DevTools > Memory
node --heap-prof node_modules/next/dist/bin/next build

The debug mode doesn't work with the webpack build worker. In the profile, look for one package that owns a large share of allocations: an icon set imported whole, a large JSON file, a charting library pulled into every page. The bundle analyzer shows the same thing from the output side, and removing or lazy-loading one large dependency often does more than any setting above.

What happens on EthioDeploy?

EthioDeploy builds your app in a separate build container, so a build that runs out of memory fails the deploy and the previous version keeps serving traffic. The build log shows the same FATAL ERROR line or an exit code 137 as anywhere else, and the fixes above apply as written.

Put --max-old-space-size in the build script, not in a NODE_OPTIONS environment variable. Project environment variables reach both the build and the running app, and on the Free plan the app container caps the Node heap below its 256 MB memory limit so Node collects garbage before the kernel kills it. A --max-old-space-size of your own in NODE_OPTIONS replaces that cap, so a value sized for the build would also apply to the 256 MB server. The deploy log says when the cap is applied.

If your app needs more memory at runtime too, see pricing for plan limits, and Deploy Next.js for the full setup.

Questions

Raise the Node.js heap limit for the build only, for example "build": "node --max-old-space-size=3072 node_modules/next/dist/bin/next build" in package.json, with the number below the memory the build machine has. Then cut what the build uses: turn off source maps, run the type check in CI instead of in next build, and on webpack builds turn on experimental.webpackMemoryOptimizations.

Below the memory the build machine or container actually has, leaving room for memory outside the heap. Node's docs suggest 1536 on a machine with 2 GiB. A value above the real memory lets V8 grow until the kernel kills the build with exit code 137.

Exit code 137 means the kernel killed the process because the container or machine ran out of memory. That is not the heap limit. Raising --max-old-space-size won't help and can cause it. Use less memory in the build or build on a machine with more.

Next.js 16 builds with Turbopack by default. Turbopack does most of its work in native code rather than in the Node.js heap, but the TypeScript check and static page generation still run in Node, so heap errors can still happen there.

Only if the type check runs somewhere else, such as tsc --noEmit in CI. Otherwise type errors reach production. The Next.js docs recommend promoting a build only after static analysis has passed.

Sources

Checked on November 3, 2026:

  • Next.js: Memory usage guide
  • Next.js: next CLI (build --webpack)
  • Next.js: Turbopack
  • Node.js: --max-old-space-size
  • Chrome DevTools: Record heap snapshots

Push a repo. Get a URL.

  • Deploy Next.js
  • Troubleshooting deploys
  • Pricing and plan limits
Start free

Join the EthioDeploy Telegram

Release notes as they ship, plus deploy help from the team and other developers.

Join on Telegram
BS
WRITTEN BYBrook SolomonFounder, EthioDeploy

Brook Solomon founded EthioDeploy and builds its deploy pipeline, build servers and AI coding agent. The posts here come from errors seen in real deploys on the platform.

All posts by Brook →
On this page
  • What does JavaScript heap out of memory mean?
  • Is it the heap or the whole machine?
  • How do I raise the heap limit for next build?
  • Which build step runs out of memory?
  • Building with webpack?
  • How do I see what is using the memory?
  • What happens on EthioDeploy?
  • Questions
  • Sources
0% read

Keep reading

  • /* → index.html

    Fix deploy errorsNov 4, 2026

    404 on page refresh in a React or Vite SPA

    A single-page app returns 404 on refresh because the browser asks the server for a path like /dashboard, and the server has no file at that path. Your router only exists in the JavaScript that index.html loads, so the server has to answer every unknown path with index.html and let the router take over. It works locally because Vite's dev server and vite preview already do that. In production you add one rewrite rule to your host: send every path that isn't a real file to /index.html.

  • code=H10

    Fix deploy errorsNov 2, 2026

    Heroku H10 App crashed: causes and fixes

    Heroku logs H10 App crashed when a request reaches your app and every web dyno has crashed. H10 is the symptom, not the cause. The real error is in the app's own log lines just above the router line, and it is usually one of five things: the app didn't bind to $PORT within 60 seconds, the Procfile is wrong, a package was pruned with devDependencies, a config var or database is missing at boot, or the dyno ran out of memory.

  • VITE_*

    Fix deploy errorsOct 29, 2026

    Vite env variables undefined in production: causes and fixes

    A Vite env variable is undefined in production when the build never saw it. Vite replaces every import.meta.env.VITE_ reference with a fixed value while vite build runs, so a variable added to your server afterwards changes nothing until you build again. The other common causes are a name without the VITE_ prefix and code that reads process.env in the browser.

EthioDeploy

Agentic first development and deployment, started in Ethiopia, built for developers everywhere.

Telegram community
Product
PricingAI coding agentAgent mode docsDomainsCLIFree backend hostingHosting in EthiopiaChangelog
Deploy guides
Next.jsNode.jsPythonSvelteKitTanStack StartDjangoFastAPIFlaskExpressNestJSLaravelRailsGoTelegram botLovableBoltReplitv0Google AI StudioBase44Firebase StudioClaude Code & Cursor
Compare
vs Herokuvs Vercelvs Rendervs Railwayvs Fly.iovs NetlifyHeroku alternativesVercel alternativesNetlify alternativesRender alternativesRailway alternativesFly.io alternativesReplit alternatives
Resources
DocsBlogFree toolsAboutSupportContactllms.txt
© 2026 EthioDeployPay by card in USD or bank transfer in ETBTermsPrivacy