A minimal Docker image with just Node.js.
The image starts from scratch and contains only a fully static Node.js binary compiled from source with build.sh. There is no OS, shell, or package manager. Multi-arch manifests are published for linux/amd64 and linux/arm64.
Images are published for the latest Node.js Current release and the latest release of every supported LTS line (Active and Maintenance LTS). A daily job checks for new releases and builds any that haven't been published yet. Every build is available from both registries with the following tags:
| Tag | Description |
|---|---|
24.21.0 |
Exact Node.js version |
24 |
Major version (latest release of that major) |
krypton |
LTS codename (latest release of that LTS line) |
lts |
The latest Active LTS release |
current |
The latest Node.js Current release |
latest |
The highest Node.js version published |
LTS lines are built until they reach end-of-life. After that, their major and codename tags stop receiving updates, so switch to lts or a newer major to keep getting security fixes.
latest is usually the same image as current. When a major version has just entered LTS and the next major has not been released yet, there is no Current release: latest follows the new LTS release and current stays on the last Current release until the next major ships.
This image is published to Docker Hub:
https://hub.docker.com/r/chorrell/node-minimal
To pull the latest node-minimal image:
docker pull chorrell/node-minimal:latestTo pull a specific version:
docker pull chorrell/node-minimal:20.10.0This image is published to the GitHub Container Registry:
https://github.com/chorrell/docker-node-minimal/pkgs/container/node-minimal
To pull the latest node-minimal image:
docker pull ghcr.io/chorrell/node-minimal:latestTo pull a specific version:
docker pull ghcr.io/chorrell/node-minimal:20.10.0The entrypoint is the Node.js binary itself, so arguments are passed straight to Node:
docker run --rm chorrell/node-minimal:latest -e "console.log('Hello from Node.js ' + process.version)"To run a script, mount it into the container and pass its path:
docker run --rm -v "$PWD:/app" -w /app chorrell/node-minimal:latest app.jsEvery published image and the compiled Node.js binary are signed with a GitHub artifact attestation: an in-toto provenance statement signed with a Sigstore-issued certificate that binds the artifact to the repository, workflow run, and commit that produced it. Verified with the GitHub CLI:
gh attestation verify oci://ghcr.io/chorrell/node-minimal:latest -R chorrell/docker-node-minimal
gh attestation verify oci://docker.io/chorrell/node-minimal:latest -R chorrell/docker-node-minimalThe images additionally carry Docker Buildx provenance and an SBOM embedded in the image, inspectable with OCI tooling:
docker buildx imagetools inspect ghcr.io/chorrell/node-minimal:latest --format "{{json .Provenance}}"
docker buildx imagetools inspect ghcr.io/chorrell/node-minimal:latest --format "{{json .SBOM}}"Node.js is compiled from source as a fully static binary by build.sh, then copied into the scratch image by the Dockerfile:
./build.sh -n 20.10.0
cp node-src/out/Release/node node
docker build -t node-minimal .See AGENTS.md for the full development guide and SETUP.md for local pre-commit hook setup.
build.sh configures Node with --fully-static, which appends -static to every link command in the generated makefiles. That includes build-time host tools and shared libraries that only work when linked dynamically, and upstream this remains broken (nodejs/node#41497; the fix proposed in nodejs/node#30199 was never merged). The build works today because:
--without-intlsetsv8_enable_i18n_support=0, which keepsgen-regexp-special-case— a V8 host tool that segfaults when linked with-static(nodejs/node#30180) — out of the defaultmakedependency graph, so it is never built or run.- Node 18.0.0 removed the
test_crypto_enginetest fixture from the default build (nodejs/node#41830). It is a shared library, and-sharedcannot be combined with-static— the linker error in #41497. It is now only built when running the test suite.
This repo previously worked around both by stripping -static from those targets' generated makefiles after ./configure, as suggested in nodejs/node#41497 (comment). Since Node 18, neither target participates in the default build, so the patch was removed.
If the static build fails again after dropping --without-intl — or after a future Node release reintroduces a shared library into the default build — expect a linker error like crtbeginT.o: relocation ... can not be used when making a shared object or a gen-regexp-special-case crash during make. The fix is to re-apply the patch: after ./configure, strip -static from the affected generated makefiles (for example out/tools/v8_gypfiles/gen-regexp-special-case.target.mk) before running make.