This repository contains experiments for building Node.js with V8 pointer compression enabled. Pointer compression is a V8 optimization that reduces memory usage by using 32-bit compressed pointers instead of full 64-bit pointers.
Pre-built multi-architecture images (amd64/arm64) are available on DockerHub:
# Pull the latest image (Debian trixie, recommended)
docker pull platformatic/node-caged:latest
# Or use a specific variant
docker pull platformatic/node-caged:trixie # Full Debian
docker pull platformatic/node-caged:slim # Minimal Debian
docker pull platformatic/node-caged:alpine # Alpine Linux (experimental)
# Pin to a major Node.js version (recommended for most users)
docker pull platformatic/node-caged:26
docker pull platformatic/node-caged:26-slim
docker pull platformatic/node-caged:25
docker pull platformatic/node-caged:25-slim
# Pin to an exact Node.js version
docker pull platformatic/node-caged:25.6.1
docker pull platformatic/node-caged:25.6.1-slimBoth Node.js 25.x and 26.x are published. Floating tags (latest, trixie, slim, alpine) track the highest published major (currently 26).
| Tag | Description |
|---|---|
latest, trixie |
Latest build on Debian trixie for the highest major (recommended) |
slim |
Minimal Debian trixie-slim runtime for the highest major |
alpine |
Alpine Linux with musl libc for the highest major (experimental) |
{major} |
Latest patch of major version on trixie (e.g., 25, 26) |
{major}-{variant} |
Latest patch of major version with variant (e.g., 25-slim, 26-alpine) |
{version} |
Exact Node.js version on trixie (e.g., 26.1.0) |
{version}-{variant} |
Exact version and variant (e.g., 26.1.0-alpine) |
| Variant | Base Image | Size | Compatibility |
|---|---|---|---|
trixie |
debian:trixie | ~250MB | Full glibc, best compatibility |
slim |
debian:trixie-slim | ~150MB | Minimal glibc runtime |
alpine |
alpine:3.21 | ~100MB | musl libc, experimental |
Build the Docker image:
docker build --network=host -t node-pointer-compression .Run the tests:
./run-tests.shMemory comparison between standard Node.js 22 and pointer-compressed Node.js 25 (with --expose-gc):
| Data Structure | Standard Node 22 | Pointer Compressed | Savings |
|---|---|---|---|
| Array of Objects (1M items) | 40.47 MB (42.43 B/item) | 20.24 MB (21.22 B/item) | 50% |
| Nested Objects (500K items) | 50.21 MB (105.29 B/item) | 24.64 MB (51.68 B/item) | 51% |
| Linked List (500K items) | 19.08 MB (40.01 B/item) | 9.54 MB (20.01 B/item) | 50% |
| Array of Arrays (500K items) | 38.76 MB (81.28 B/item) | 19.38 MB (40.64 B/item) | 50% |
- Pointer compression delivers consistent ~50% memory reduction across all pointer-heavy data structures
- Bytes-per-item is almost exactly halved, matching the theoretical expectation (32-bit vs 64-bit pointers)
- Baseline memory usage is also lower (2.11 MB vs 3.74 MB)
- Heap limit: 4GB per V8 isolate. Each worker thread has its own 4GB limit, so you can exceed 4GB total using multiple workers (e.g., main + 4 workers = 20GB max)
- Compatibility: Requires building Node.js from source with
--experimental-enable-pointer-compression
N-API addons work correctly with pointer compression. Tested and verified:
| Addon | Type | Status |
|---|---|---|
bcrypt |
N-API | ✓ Works |
sharp |
N-API | ✓ Works |
@napi-rs/uuid |
Rust N-API | ✓ Works |
@node-rs/argon2 |
Rust N-API | ✓ Works |
Non-N-API native addons may crash. Addons using the older V8 native addon API (like better-sqlite3) are not compatible with pointer compression and can segfault. Packages that rely on nan are especially affected. Always prefer N-API-based alternatives.
If your dependency uses nan and does not provide binaries built for this runtime, you must force a local rebuild of its native addon.
If the package in node_modules still contains source files, try a normal rebuild:
cd node_modules/<package-name>
pnpm install --ignore-scripts
pnpm run rebuildSome packages on npm do not ship source files needed to rebuild. In these cases, you may need to fetch the source from the git repository, copy it back into node_modules, and run a manual rebuild.
This was tested against @datadog/pprof 5.13.5.
cd node_modules/@datadog/pprof
pnpm install --ignore-scripts
PPROF_VERSION=$(node -p "require('./package.json').version")
PPROF_REPO=$(node -p "require('./package.json').repository.url.replace('git+', '')")
git clone -q --depth=1 --branch=v$PPROF_VERSION $PPROF_REPO /tmp/pprof-nodejs > /dev/null 2>&1
mv /tmp/pprof-nodejs/{binding.gyp,bindings} .
rm -rf /tmp/pprof-nodejs
pnpm run rebuild
cd ../../..The Dockerfiles build Node.js from the v25.x or v26.x branch (selected via the NODE_VERSION build arg) with the --experimental-enable-pointer-compression configure flag. This enables V8's pointer compression feature which uses 32-bit offsets from a base address instead of full 64-bit pointers.
tests/verify-pointer-compression.js- Verifies pointer compression is enabled by checking heap limitstests/memory-benchmark.js- Benchmarks memory usage with pointer-heavy data structurestests/worker-heap-limits.js- Verifies each worker thread has its own 4GB heap limittests/napi-addon-test.js- Tests N-API native addon compatibility (requires npm install)
For local development, use the root Dockerfile:
# Build for local architecture
docker build --network=host -t node-pointer-compression .
# Run interactively
docker run -it node-pointer-compression
# Run a script
docker run -v $(pwd):/app node-pointer-compression node /app/your-script.jsTo build a specific variant locally:
# Build trixie variant
docker build -f docker/trixie/Dockerfile -t node-pointer-compression:trixie .
# Build slim variant
docker build -f docker/slim/Dockerfile -t node-pointer-compression:slim .
# Build alpine variant
docker build -f docker/alpine/Dockerfile -t node-pointer-compression:alpine .The GitHub Actions workflow builds and publishes multi-architecture images:
- Trigger: Manual only (
workflow_dispatch) - Major selection: Choose
25,26, orall(default) when dispatching - Version detection: Automatically detects the latest release for each selected major
- Duplicate check: Skips a major if its current version already exists on DockerHub
- Force rebuild: Option to bypass the version check and rebuild
Images are built natively on both amd64 and arm64 runners for optimal build performance.