Packages with native add-ons need a build toolchain such as Python, make and g++, which the node alpine image does not include. Install those tools in a separate build stage, compile there, and copy only the finished node_modules into the final stage, so the compilers never ship to production.
What is actually happening
Alpine images are small on purpose. The official Node.js image documentation describes the alpine variant as useful when final image size is your primary concern, with the caveat that it uses musl libc instead of glibc, so software may run into issues depending on its libc assumptions. It also notes that extra tools such as git or bash are uncommon in Alpine based images.
When an npm package contains C or C++ code, npm runs node-gyp to compile it. That needs Python, make and a C++ compiler. Without them the install fails, often with a long error that mentions node-gyp.
Two fixes, one better
The Node.js maintainers document both of these.
Option one installs the toolchain as a virtual package, runs the install, then removes the toolchain in the same layer:
FROM node:alpine
RUN apk add --no-cache --virtual .gyp python3 py-setuptools make g++ \
&& npm install \
&& apk del .gyp
Option two uses a multi-stage build. The toolchain lives only in the builder:
FROM node:alpine AS builder
RUN apk add --no-cache python3 make g++
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:alpine AS app
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
The second is cleaner to read and audit. The compilers are not in any layer of the final image, and a reviewer can see that at a glance.
Check whether you need the native build at all
Many packages ship prebuilt binaries for common platforms. Those binaries are usually built against glibc, so they may not load on musl. That mismatch is a frequent reason alpine installs behave differently from a Debian slim install. If a native dependency causes repeated trouble, a Debian based slim image is a legitimate answer rather than a failure.
| Situation | Reasonable choice |
|---|---|
| Pure JavaScript dependencies | Alpine or slim, either works |
| A few native add-ons, builds fine | Alpine with a builder stage |
| Native add-ons with glibc only binaries | Debian slim |
A security angle
Leaving python3 and g++ in the final image gives an attacker compilers. It also adds packages that scanners report, which you then triage for software the app never uses. Keeping them out is a small win for both.
Where Safeguard fits
Whichever base you pick, you need to know what is inside it. Safeguard builds an SBOM and scans dependencies, with reachability analysis to separate findings in code you call from findings in code you do not. The free scan takes a repository and returns a first dependency view.
Quick check
Does the slim image avoid this problem?
Not automatically. The slim variants contain only the packages needed to run Node.js, so compiling native add-ons still needs a toolchain. The difference is glibc compatibility with prebuilt binaries.
Is deleting the toolchain in the same RUN enough?
It keeps that layer clean, but a multi-stage build is easier to verify because the final image never contains the tools in any layer.
Sources
Last reviewed: 19 October 2026.