Skip to content
Latchkey

Node "prebuilt binary not found for musl" (Alpine) in CI

A native package publishes glibc prebuilds, but Alpine uses musl libc. With no musl prebuild and no compiler, the install fails on the libc mismatch.

What this error means

On an Alpine-based runner, a native dependency fails with "prebuilt binary not found" or a runtime "Error loading shared library ... musl". A Debian/Ubuntu runner installs the same package fine.

node
Error: prebuilt binary not found for pkg on linux-x64-musl

Error loading shared library ld-linux-x86-64.so.2: No such file or directory
(needed by /work/repo/node_modules/pkg/build/Release/addon.node)

Diagnose it: reproduce the CI install locally

Install failures are usually environment drift rather than a broken lockfile: a different package-manager major, a different Node version, or a cache that is being restored from a run with different inputs. Reproduce the CI conditions before changing the lockfile, because regenerating it hides the real cause.

Terminal
# match the runner exactly, then install from a clean slate
node --version && npm --version
rm -rf node_modules
npm ci --foreground-scripts

# if that succeeds locally but fails in CI, the difference is the cache
# or the package-manager version, not your lockfile

Common causes

Package ships glibc-only prebuilds

The native package does not publish a musl variant, so Alpine has nothing compatible to download.

Alpine lacks the build toolchain to compile

Without build-base and python3, the source fallback cannot run on Alpine.

How to fix it

Use a glibc base image

Switch the runner image to a glibc distro (Debian/Ubuntu slim) so the published prebuilds apply.

Dockerfile
# Dockerfile
FROM node:20-bookworm-slim
# instead of node:20-alpine

Install musl build tools to compile from source

If you must stay on Alpine, add the toolchain so the native module builds for musl.

Terminal
apk add --no-cache build-base python3
npm ci

Verify the fix survives a cold cache

A green run immediately after a fix often proves nothing, because it restored a cache written before the change. Force a cold install once to confirm the fix is real.

.github/workflows/ci.yml
# temporarily bust the cache key to prove the fix on a cold runner
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm
    cache-dependency-path: package-lock.json
# then bump this suffix once, run, and remove it
#   key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-v2

How to prevent it

  • Prefer glibc base images for projects with native dependencies.
  • Add build-base + python3 when Alpine is required.
  • Latchkey self-healing managed runners auto-retry transient prebuild download failures and offer glibc base images so musl/glibc mismatches do not block installs.

Frequently asked questions

What causes Node "prebuilt binary not found for musl" (Alpine) in CI?
There are 2 common causes: package ships glibc-only prebuilds and alpine lacks the build toolchain to compile. The native package does not publish a musl variant, so Alpine has nothing compatible to download.
How do I fix Node "prebuilt binary not found for musl" (Alpine) in CI?
There are 2 fixes depending on which cause you have: use a glibc base image and install musl build tools to compile from source. Work through them in order, since the first is the most common.
What does Node "prebuilt binary not found for musl" (Alpine) in CI actually mean?
On an Alpine-based runner, a native dependency fails with "prebuilt binary not found" or a runtime "Error loading shared library ...
How do I stop Node "prebuilt binary not found for musl" (Alpine) in CI happening again?
Prefer glibc base images for projects with native dependencies. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card