Why a compiled add-on cares about the runtime
Some npm packages contain compiled code that Node.js loads as an add-on. Node's documentation says Node-API is intended to be ABI stable across Node.js versions, so an add-on built only on it should keep working on later major versions without recompiling. The same page says the C++ interfaces, libuv and V8 give no such stability across majors, so an add-on that uses them directly has to be rebuilt, or upgraded to a release built for the new line. A package can therefore install quietly on one runtime and fail on the next.
- A failure usually shows at install time (a build error) or at load time (a version error).
- Whether a given package uses Node-API is for its maintainers to state; do not assume.
Find them before you change the runtime
In a copy of the project with dependencies installed, look for binding.gyp files and files ending in .node inside the installed packages, and read the install log for compiler output. Record each package that contains one, with its version and the version its maintainers say supports your target. Some packages fetch a prebuilt binary and others compile on install; the log tells you which.
- Run the search in the same kind of image your host builds, not only on a laptop.
- Include packages used only by build tools, not just runtime dependencies.
If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.
find node_modules -name binding.gyp -o -name "*.node" | head -20Why it passes on a laptop and fails in the image
A developer machine may already have a compiler and system libraries that a minimal container image lacks, and the two may select different package versions if a lockfile is not honoured. A clean install from the lockfile inside the build image removes that difference. If the image needs a compiler or library to build a module, that is a change to the image, and it is part of the job.
- Do not fix an image failure by installing extra tools on one developer machine.
- Keep the lockfile in version control and install from it.
Decide each module on the record
For each compiled module choose one of four outcomes: rebuild it unchanged, upgrade it to a release that supports the target, replace it with another package, or remove the feature. Write the reason next to each. If a module has no release for the target and nothing maintained replaces it, the runtime move cannot be completed as a small change and the fixed job stops.
- A replacement must be tested against the inputs the app really uses.
- An abandoned module is a finding for the owner, not a detail.
How acceptance works
In the fixed Node.js 24 LTS job each native module found is listed with its outcome and a load result on the target, the install and tests pass from a clean lockfile install, and the engines entry and image agree. The npm docs note that engines is advisory by default, so the job checks what actually runs rather than relying on that field. Send the names of any compiled packages, not file contents. Prices are untested proposals with payment after the agreed checks.
Sources and limits
- Node.js documentation: Node-API Checked 2026-10-11.
- Node-API is intended to be ABI stable across Node.js versions, so a module compiled for one major version should run on later major versions without recompilation.
- The Node.js C++ APIs, libuv and V8 do not offer ABI stability across major versions.
- npm docs: package.json engines Checked 2026-10-11.
- The engines field declares intended Node.js versions and is advisory unless engine-strict is turned on.