Custom builds of Node.js with V8 pointer compression enabled. Pointer compression shrinks V8 heap pointers from 8 bytes to 4 bytes, reducing heap memory usage significantly for applications with many heap-allocated objects.
Prebuilt binaries for all platforms are published as GitHub Releases. Each release is triggered manually via the "Build Node.js from Source" Actions workflow.
The build steps live in the reusable build.yaml workflow. The Node.js version to build is set in node-version.txt, which both CI and build.sh read. It holds the tag of one of the official Node.js releases. Pull requests that touch the version, patches, scripts or build workflows start "Test Node.js build", which runs the same build on all platforms without uploading artifacts or publishing a release. Because a full build takes hours, each run waits for a maintainer to approve it (the test-build environment has required reviewers). It can also be started manually from the Actions tab.
All builds are configured with:
| Flag | Effect |
|---|---|
--experimental-enable-pointer-compression |
Compress V8 heap pointers from 8 → 4 bytes |
--experimental-pointer-compression-shared-cage |
Share the compression cage across Isolates in the same process; this also enables the V8 sandbox (v8_enable_sandbox) |
--without-siphash |
Use a simpler alternative to SipHash for V8 hash seeds |
| Platform | Architecture | Runner |
|---|---|---|
| Linux | x64 | ubuntu-22.04 |
| Linux | arm64 | ubuntu-22.04-arm |
| macOS | x64 | macos-15-intel |
| macOS | arm64 | macos-15 (Apple Silicon) |
| Windows | x64 | windows-2025 |
| Windows | arm64 | windows-11-arm |
Patches applied on top of the Node.js source tree live in patches/, in the order listed in patches/.patches. Each patch's commit message explains why it is needed. The list can be empty: upstream Node.js now supports building with the V8 sandbox (nodejs/node#62237), which pointer compression requires, so a plain release may build without any patches.
Electron builds its bundled Node.js with pointer compression and the V8 sandbox enabled, so its Node.js patch set is the place to look when a Node.js release needs fixes for this configuration. Take patches from an Electron release whose node_version in DEPS matches node-version.txt. Most of Electron's patches are Electron-specific, or adapt Node.js to the newer V8 that Chromium ships; these builds use the V8 bundled with Node.js, so they don't need them. The git-import-patches and git-export-patches scripts come from Electron too.
Run ./build.sh once to get a patched source tree in node-src-<tag>/, then:
cd node-src-<tag>
vim some/code/file.cc
git commit
../script/git-export-patches -o ../patchesNote
git-export-patches ignores uncommitted files — create a commit first. The commit subject line becomes the patch file name; the body should explain why the patch exists.
Re-exporting patches may cause shasums in unrelated patches to change. This is harmless — include those changes in your PR anyway to keep things consistent for others.
build.sh builds on macOS and Linux; Windows builds only run in CI (through vcbuild.bat).
- macOS: Xcode 16.4 or newer, plus
ninja(brew install ninja). - Linux: Clang 19 (
clang-19from apt.llvm.org, which the script picks up when it is installed) or another compiler that meets Node.js's requirements, plusmakeandninja-build. - On both:
gitand Python 3.
./build.shOn macOS, if a Homebrew LLVM clang comes first on your PATH, point the build at Apple clang to match CI:
CC=/usr/bin/clang CXX=/usr/bin/clang++ ./build.shThe script:
- Clones Node.js at the tag pinned in
node-version.txtintonode-src-<tag>/and applies the patches frompatches/. - Configures with the same flags as CI (plus
--openssl-use-def-ca-storeon macOS). - Builds with Ninja on all cores. The first build takes a while.
- Smoke tests
nodeandnpm, then installs into./release/usr/local/node-<tag>-<platform>-<arch>/(the same layout as the release tarballs).
./release/usr/local/node-<tag>-<platform>-<arch>/bin/node --versionTo confirm pointer compression is on, this prints 1:
./release/usr/local/node-<tag>-<platform>-<arch>/bin/node -p "process.config.variables.v8_enable_pointer_compression"Pointer compression also caps the V8 heap at about 4 GB, so this prints at most about 4096 (MB):
./release/usr/local/node-<tag>-<platform>-<arch>/bin/node -p "v8.getHeapStatistics().heap_size_limit / 2**20"Later runs reuse node-src-<tag>/ and build incrementally. If patches/ has changed since the patches were applied there, the script resets the tree to the tag and applies them again. Uncommitted edits are built as they are. It stops without touching anything when the tree has commits that were never exported, or uncommitted edits while the patches need re-applying, and says how to export or discard them.
To start from scratch, delete node-src-<tag>/ and release/.