You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Production build peak memory +53% after upgrading @angular/build 21.2.13 → 22.0.0 (same code, same esbuild version) #33368
After upgrading to Angular 22, our production build's peak memory rose from ~15.5GB to ~23.8GB (+53%), which started OOM-killing our 16GB CI runners. A/B measurement on the identical commit of a large Nx monorepo (~7,400 TS
files, ~2,250 components, single application target), cold build cache, default workers, sampling combined RSS of the full node + esbuild process tree every 3s:
Toolchain │ Peak RSS │
@angular/build 21.2.13, Angular 21.2.15, TS 5.9.3 │ 15.5GB │
@angular/build 22.0.0, Angular 22.0.0, TS 6.0.3 │ 23.8GB │
@angular/build 22.0.1, Angular 22.0.0, TS 6.0.3 │ 22.3GB │
esbuild is 0.28.0 in both setups (unchanged).
The application code is identical in both runs (the v21 build needed only ignoreDeprecations adjusted in tsconfig).
NG_BUILD_MAX_WORKERS=2 makes peak memory worse (31.8GB), not better.
Caveat: TypeScript necessarily moved 5.9.3 → 6.0.3 with the Angular major, so we cannot separate the TS 6 share of the increase from @angular/build 22 itself.
Symptom on constrained machines: host OOM killer fires (or esbuild dies with fatal error: all goroutines are asleep - deadlock! when Node's --max-old-space-size exceeds physical RAM).
Minimal Reproduction
Private repo, so I can't share a reproduction, but happy to run instrumented builds / --stats-json / heap profiles on request.
I've run into the same issue but already with Angular 21.2.15.
After we upgraded from @angular/build 21.2.13 -> 21.2.15 (Angular 21.2.15 -> 21.2.17), our Docker build in GitHub Actions (ubuntu-latest with 8GB) as well as locally in Docker Desktop (limited to 8GB) started failing with either Docker OOM or within esbuild fatal error: all goroutines are asleep - deadlock!.
After increasing the RAM limit to 16 GB for our local Docker Desktop environment and using a larger runner in GitHub, the build succeeds again.
Reacted by Dmitriy Stepanenko, rubiesonthesky, Vincent Lycoops and Dmytro Tkachenko
Environment: Angular 22 (@angular/build@22.0.0), CI on ubuntu-latest (2 CPUs, 7.9GB RAM). App is in the Nx monorepo
We tested with a real production app (1898 lazy chunks) and a mock app (21 / 22) with a large number of dummy routed components to reproduce the issue in isolation (2500 chunks)
Observed: Builds started OOMing after migrating from ng21. Error: The operation was canceled. Even with NG_BUILD_OPTIMIZE_CHUNKS=false the build fails — system reaches ~7.8GB (98%) leaving ~160MB free, which is not enough for post-build operations.
We added phase logging + system memory monitor (/proc/meminfo) to execute-build.js to capture timing and memory at each build phase.
Key data measured on CI (mock app, 2500 chunks):
ng21
ng22
bundleAll:done sys
6913MB
6254MB (ng22 lower, actually confusing)
optimizeChunks duration
2.3s
19.1s (8x slower)
heap spike during opt
+20MB
+1627MB
sys spike during opt
+533MB
+1490MB
peak sys (opt ON)
7446MB (free=491MB)
7744MB (free=193MB)
peak sys (opt OFF)
7395MB (free=547MB)
~7780MB (free=~160MB)
Two separate regressions:
optimizeChunks is now default-on and 8x more expensive in memory and time — the heap spikes from +20MB to +1627MB for the same chunk count.
Even with NG_BUILD_OPTIMIZE_CHUNKS=false, ng22 uses ~350-400MB more system memory than ng21 during bundleAll. ng21 completes with ~547MB free; ng22 completes with ~160MB free — not enough for any subsequent process allocation.
Our production app fails on ng22 even with chunk optimization explicitly disabled (NG_BUILD_OPTIMIZE_CHUNKS=false), while ng21 with chunk optimization fully enabled passes comfortably. The gap between them is ~350MB in the bundleAll phase alone.
Workaround that partially helps:NG_BUILD_OPTIMIZE_CHUNKS=false removes the large spike and allows the Angular build itself to complete, but post-build steps still get OOM-killed due to the baseline regression.
As the topic starter, I can't share the production app's code, but I'll be happy to run any measurements that may help debugging it.
Reacted by Artur, profanis, Giorgi Cheishvili, Dmytro Tkachenko and Sudipto Mukherjee
The crash is caused by the chunk optimizer, not TypeScript compilation or bundling itself
The app produces 1160 lazy chunks (esbuild) / 1338 lazy chunks (rolldown) — with threshold=3, the optimizer evaluates a very large number of merge candidates
With esbuild, the JS heap grows to 4 GB+ during optimizeChunks; with rolldown the same pass completes at ~409 MB heap
Disabling chunk optimization (NG_BUILD_OPTIMIZE_CHUNKS=0) avoids the OOM entirely but leaves 1160 unmerged lazy chunks (initial bundle: 3.33 MB vs 1.21 MB with rolldown)
Rolldown also produces better-merged output (1.21 MB initial) suggesting its chunk graph representation is cheaper to optimize
Reacted by Dmitriy Stepanenko, profanis, Leigh Penny Jr., Giorgi Cheishvili, Dmytro Tkachenko and Sudipto Mukherjee
@arturovt The bundler is always esbuild. The NG_BUILD_CHUNKS_ROLLDOWN option controls whether the post-build chunk optimizer stage uses either rollup (default for v22.0) or rolldown (upcoming default for v22.1 now that it is stable).
Also, 22.0.2 contains a performance optimization (#33371) which should provide noticeable memory usage improvements outside of the chunk optimizer area. Please update to the latest patch to take advantage. Also @t-barak, if you have some updated numbers for your project with 22.0.2+ for comparison, that would be very helpful.
@clydin here's the comparison of ng21 with the latest, done on my machine for our 2 largest apps in the workspace. On Github Actions CI it OOMs, so it's not informative
If it helps, provide me with patches for angular packages with logging you might be interested in.
Local Build Memory Comparison
Machine: macOS, 48 GB RAM Node options:--max_old_space_size=8192 Config:app-1:build:production / app-2:build:production Flags:NX_SKIP_NX_CACHE=true NG_BUILD_OPTIMIZE_CHUNKS=false NG_BUILD_MAX_WORKERS=1 Cache:.angular/cache and dist/ cleared before each run
bundleAll:done rss=4061 MB
NX yield rss=4043 MB <= TS worker still alive
onDispose fires here (post-yield)
Below is AI analysis of everything I was able to extract:
Root cause
compilation.close() terminates the TypeScript compilation worker thread, which holds ~3 GB of the TypeScript program (ASTs, type checker, source file cache). In ng21 this cleanup was implicit — esbuild.build() (non-incremental) schedules onDispose via setTimeout(cb, 0) which fires before the async yield chain propagates up. Memory is freed before NX sees the result.
In ng22, @angular/build introduced explicit result.dispose() in build-action.js (added in the new non-watch finally block). This is the right pattern for resource management, but the timing is wrong — it runs after yield* emitOutputResults() and after the NX executor receives and processes the result. The TS worker stays alive for the entire post-bundle phase and during file writing to disk.
The delta between ng21 and ng22 at yield is ~3 GB, which is exactly the size of compilation.close() cleanup.
Potential Fix
In @angular/build's build-action.js, call result.dispose() (or at minimum compilation.close()) before yield* emitOutputResults() in non-watch mode. The disposal sequence needs to move from post-yield to pre-yield.
This is an @angular/build issue — NX's executor just forwards yields and has no control over disposal timing.
Reacted by Dmytro Tkachenko, Josh and Sudipto Mukherjee
This is actually a show-stopper for us, we had to revert the update to Angular 22.
CI systematically crashes and with RAM prices sky rocketing, migrating our self-hosted solution becomes an expensive endeavour.
I have the same issue, even for our smaller projects RAM increase has been substantial. We've also had to bump up the size of our runners as well. ng serve is also sluggish. The angular team need to define tolerances for build performance.
Reacted by Dmitriy Stepanenko, Tomas Rimkus and Ryan Hutchison
If increasing memory of your machines is not feasable right now, you can consider running a custom node.js build with ./configure --experimental-enable-pointer-compression and halve node.js memory usage. See also blog.platformatic.dev/we-cut-nodejs-memory-in-half
The catch: If any of your workloads need more than 8gb of RAM, this wont work for you, as that flag caps memory at 4gb per node.js worker (a 8gb heap becomes a 4gb heap with pointer-compression, hence the 4gb limit can be seen as a 8gb limit if you are measuring a "normal node.js" thread).
It's not fully official yet. If you want to know why you can read nodejs/build#4352 and follow the breadcrumbs all the way back to 2020. But maybe if more people use it, it may gain traction.
Of course if angular cli could use less memory that would be very welcome too. But using pointer-compression and fixing memory issues aren't mutually exclusive.
I haven't tried, but pointer compression restricts the processes to use a maximum of 4GB. Builds that are affected by this issue will generally exceed this memory usage naturally.
After conducting several performance tests, it has been observed that Angular version 22.1.0 shows improvements, but it still doesn't reach the performance of the latest version, 21.
Attached are graphs of a service that waits for pre-rendered build triggers (where an increase in resource consumption was detected after the update).
The CPU and memory spikes occur because the service starts (performs a pre-rendered build) and immediately receives a build trigger, which is normal.
Is it possible to control Node.js resource consumption during the Angular build process so that a pre-rendered build doesn't consume so many resources?
Activity
ffried commentedon Jun 17, 2026
I've run into the same issue but already with Angular 21.2.15.
After we upgraded from @angular/build 21.2.13 -> 21.2.15 (Angular 21.2.15 -> 21.2.17), our Docker build in GitHub Actions (ubuntu-latest with 8GB) as well as locally in Docker Desktop (limited to 8GB) started failing with either Docker OOM or within esbuild
fatal error: all goroutines are asleep - deadlock!.After increasing the RAM limit to 16 GB for our local Docker Desktop environment and using a larger runner in GitHub, the build succeeds again.
dmitry-stepanenko commentedon Jun 17, 2026
Environment: Angular 22 (
@angular/build@22.0.0), CI onubuntu-latest(2 CPUs, 7.9GB RAM). App is in the Nx monorepoWe tested with a real production app (1898 lazy chunks) and a mock app (21 / 22) with a large number of dummy routed components to reproduce the issue in isolation (2500 chunks)
Observed: Builds started OOMing after migrating from ng21.
Error: The operation was canceled.Even withNG_BUILD_OPTIMIZE_CHUNKS=falsethe build fails — system reaches ~7.8GB (98%) leaving ~160MB free, which is not enough for post-build operations.We added phase logging + system memory monitor (
/proc/meminfo) toexecute-build.jsto capture timing and memory at each build phase.Key data measured on CI (mock app, 2500 chunks):
bundleAll:donesysoptimizeChunksdurationTwo separate regressions:
optimizeChunksis now default-on and 8x more expensive in memory and time — the heap spikes from +20MB to +1627MB for the same chunk count.Even with
NG_BUILD_OPTIMIZE_CHUNKS=false, ng22 uses ~350-400MB more system memory than ng21 duringbundleAll. ng21 completes with ~547MB free; ng22 completes with ~160MB free — not enough for any subsequent process allocation.Our production app fails on ng22 even with chunk optimization explicitly disabled (
NG_BUILD_OPTIMIZE_CHUNKS=false), while ng21 with chunk optimization fully enabled passes comfortably. The gap between them is ~350MB in the bundleAll phase alone.Workaround that partially helps:
NG_BUILD_OPTIMIZE_CHUNKS=falseremoves the large spike and allows the Angular build itself to complete, but post-build steps still get OOM-killed due to the baseline regression.As the topic starter, I can't share the production app's code, but I'll be happy to run any measurements that may help debugging it.
arturovt commentedon Jun 17, 2026
Exactly the same issue we're facing that been described by @dmitry-stepanenko.
Build OOM investigation results (
supplierapp, 32GB RAM, Node v24.15.0)NG_BUILD_PARALLEL_TS=false npx nx build supplierNG_BUILD_OPTIMIZE_CHUNKS=0 npx nx build supplierNG_BUILD_CHUNKS_ROLLDOWN=true npx nx build supplierKey observations:
threshold=3, the optimizer evaluates a very large number of merge candidatesoptimizeChunks; with rolldown the same pass completes at ~409 MB heapNG_BUILD_OPTIMIZE_CHUNKS=0) avoids the OOM entirely but leaves 1160 unmerged lazy chunks (initial bundle: 3.33 MB vs 1.21 MB with rolldown)clydin commentedon Jun 22, 2026
@arturovt The bundler is always esbuild. The
NG_BUILD_CHUNKS_ROLLDOWNoption controls whether the post-build chunk optimizer stage uses either rollup (default for v22.0) or rolldown (upcoming default for v22.1 now that it is stable).Also, 22.0.2 contains a performance optimization (#33371) which should provide noticeable memory usage improvements outside of the chunk optimizer area. Please update to the latest patch to take advantage. Also @t-barak, if you have some updated numbers for your project with 22.0.2+ for comparison, that would be very helpful.
dmitry-stepanenko commentedon Jun 23, 2026
@clydin here's the comparison of ng21 with the latest, done on my machine for our 2 largest apps in the workspace. On Github Actions CI it OOMs, so it's not informative
If it helps, provide me with patches for angular packages with logging you might be interested in.
Local Build Memory Comparison
Machine: macOS, 48 GB RAM
Node options:
--max_old_space_size=8192Config:
app-1:build:production/app-2:build:productionFlags:
NX_SKIP_NX_CACHE=true NG_BUILD_OPTIMIZE_CHUNKS=false NG_BUILD_MAX_WORKERS=1Cache:
.angular/cacheanddist/cleared before each run@angular/buildapp-1
app-2
RSS at bundleAll:done vs NX yield
What happens between bundleAll:done and yield
I've added some logging inside esbuild's
onDisposecallback to track exactly what frees memory.ng21 — disposal happens before yield:
ng22 — disposal happens after yield:
Below is AI analysis of everything I was able to extract:
Root cause
compilation.close()terminates the TypeScript compilation worker thread, which holds ~3 GB of the TypeScript program (ASTs, type checker, source file cache). In ng21 this cleanup was implicit —esbuild.build()(non-incremental) schedulesonDisposeviasetTimeout(cb, 0)which fires before the async yield chain propagates up. Memory is freed before NX sees the result.In ng22,
@angular/buildintroduced explicitresult.dispose()inbuild-action.js(added in the new non-watchfinallyblock). This is the right pattern for resource management, but the timing is wrong — it runs afteryield* emitOutputResults()and after the NX executor receives and processes the result. The TS worker stays alive for the entire post-bundle phase and during file writing to disk.The delta between ng21 and ng22 at yield is ~3 GB, which is exactly the size of
compilation.close()cleanup.Potential Fix
In
@angular/build'sbuild-action.js, callresult.dispose()(or at minimumcompilation.close()) beforeyield* emitOutputResults()in non-watch mode. The disposal sequence needs to move from post-yield to pre-yield.This is an
@angular/buildissue — NX's executor just forwards yields and has no control over disposal timing.t-barak commentedon Jun 23, 2026
I have patched build-action.js to apply this change, and it did not make a big difference (~0.3%).
VeselyT commentedon Jun 24, 2026
We are experiencing the same issue. Since upgrading to Angular 22, we've had to switch to 32GB runners on AWS.
wartab commentedon Jun 30, 2026
This is actually a show-stopper for us, we had to revert the update to Angular 22.
CI systematically crashes and with RAM prices sky rocketing, migrating our self-hosted solution becomes an expensive endeavour.
jpike88 commentedon Jul 1, 2026
I have the same issue, even for our smaller projects RAM increase has been substantial. We've also had to bump up the size of our runners as well.
ng serveis also sluggish. The angular team need to define tolerances for build performance.dmitry-stepanenko commentedon Jul 10, 2026
@alan-agius4 @clydin any update on this?
pumano commentedon Jul 21, 2026
Any news on this?
8 remaining items
wartab commentedon Jul 30, 2026
I haven't tried, but pointer compression restricts the processes to use a maximum of 4GB. Builds that are affected by this issue will generally exceed this memory usage naturally.
alan-agius4 commentedon Jul 30, 2026
Hey @Davidgimenez1997, that commit isn't part of the 22.1.0 release. I also couldn't track down a PR for it.
Version 22.1.0 does come with a bunch of performance fixes, though could you give it a try and see if it resolves your issue?
Davidgimenez1997 commentedon Jul 31, 2026
Good morning @alan-agius4 !
After conducting several performance tests, it has been observed that Angular version 22.1.0 shows improvements, but it still doesn't reach the performance of the latest version, 21.
Attached are graphs of a service that waits for pre-rendered build triggers (where an increase in resource consumption was detected after the update).
The CPU and memory spikes occur because the service starts (performs a pre-rendered build) and immediately receives a build trigger, which is normal.
Is it possible to control Node.js resource consumption during the Angular build process so that a pre-rendered build doesn't consume so many resources?
Thank you very much for your answers!
I appreciate your feedback.
wartab commentedon Jul 31, 2026
I just attempted an upgrade to Angular 22.1 and CI still crashes, while it didn't in Angular 21:
And to further address the pointer compression suggestion, I doubt it would have any impact at all, given it's the Go process that seems to be OOMing.
perf(@angular/build): release build resources early in non-watch mode
perf(@angular/build): release build resources early in non-watch mode
perf(@angular/build): release build resources early in non-watch mode (…
perf(@angular/build): release build resources early in non-watch mode (…
wartab commentedon Aug 6, 2026
@alan-agius4 Thanks for the new Angular CLI version, it did indeed resolve the issue! Thanks a lot!
angular-automatic-lock-bot commentedon Oct 18, 2026
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.