Docker build fails when trying to set up PNPM with Node.js and NPM

I’m working with a specific base Docker image that I cannot change. I need to add Node.js, NPM, and PNPM to this container but I’m running into issues.

Here’s my current Dockerfile setup:

FROM public.ecr.aws/docker/library/docker

# Copy Node.js and NPM from official image
COPY --from=node:20.11.1 /usr/local/bin /usr/local/bin
COPY --from=node:20.11.1 /usr/local/lib/node_modules /usr/local/lib/node_modules

# Try to install PNPM globally
RUN npm install -g [email protected]

The build process breaks when it tries to install PNPM. I get this error message:

ERR [stage-0 4/12] RUN npm install -g [email protected]
> [stage-0 4/12] RUN npm install -g [email protected]:
0.131 env: can't execute 'node': No such file or directory
exit code: 127

I also tried a different approach using multi-stage builds. First I install everything in a Node.js container, then copy it over:

FROM node:20.11.1 as builder

RUN npm install -g [email protected]

FROM public.ecr.aws/docker/library/docker

# Copy Node.js, NPM, and PNPM
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules

# Create symbolic links
RUN ln -s /usr/local/bin/node /usr/bin/node
RUN ln -s /usr/local/bin/npm /usr/bin/npm
RUN ln -s /usr/local/bin/pnpm /usr/bin/pnpm

ENV PATH="/usr/local/bin:${PATH}"

WORKDIR /app

COPY ./package.json ./workspace.yaml ./
COPY ./services/api/package.json ./services/api/

RUN pnpm install --workspace-root

But I still get the same “can’t execute ‘node’” error when trying to run pnpm commands. What am I missing here?

You’re missing shared libraries that Node.js needs to run. When you copy binaries from the Node.js image, you don’t get the system dependencies that come with it. Your Docker base image probably has a different libc implementation or is missing libraries Node.js requires. I hit this same issue adding Node.js to Alpine containers. Fix it by copying the shared libraries with the binaries:

COPY --from=node:20.11.1 /usr/local/share /usr/local/share
COPY --from=node:20.11.1 /lib /lib

Or install the runtime dependencies directly in your target image. Check what Node.js runtime libraries your base image’s package manager has available. Sometimes it’s just easier to install Node.js through the system package manager instead of copying binaries between incompatible base images.

check if your binaries have execute permissions - copying between containers often strips them. add RUN chmod +x /usr/local/bin/node /usr/local/bin/npm after your copy commands. also make sure your base image has /bin/sh since npm scripts won’t run without it.

This happens because your base image and the Node.js binaries you’re copying have different architectures. I’ve hit this same issue when binaries were compiled for different architectures or glibc versions. Your docker image might be using a different architecture or musl libc, but the Node.js binaries expect glibc. Don’t copy binaries - use a package manager instead. Install Node.js through your base image’s package manager, or download the official Node.js binary that matches your architecture. Run uname -m to check your base image architecture, then grab the right Node.js tarball. You can also use the same base OS for both stages in your multi-stage build. If your final image runs on a specific Linux distro, use that same distro with Node.js already installed as your builder stage instead of the official Node image.