✦ For everyone, free.

Practical knowledge for real and everyday life

Home

16.1.2 Dependency Install Failures

A focused guide to Dependency Install Failures, connecting core concepts with practical Docker and container operations.

Dependency install failures during a Docker build are among the most common build problems encountered, and the large majority trace back to one of a small number of recognizable causes: missing system-level libraries, network or registry access issues, version incompatibilities, or a mismatch between the build environment and what a dependency assumes is already present.

Missing system-level build dependencies

Minimal base images, particularly Alpine-based ones, often lack compilers, headers, and system libraries that many language package managers assume are available when a dependency needs to be compiled from source rather than installed as a precompiled binary:

npm ERR! gyp ERR! find Python
error: Microsoft Visual C++ 14.0 is required
FROM node:20-alpine
RUN apk add --no-cache python3 make g++
RUN npm ci

Installing the specific missing system dependency, usually named directly or strongly implied by the error message, resolves this category of failure far more often than switching to a different, larger base image purely to avoid the issue, though for dependencies with extensive native compilation needs, a non-Alpine base image can sometimes be the more pragmatic choice.

Network and registry access issues

A dependency installation step that fails to reach its package registry, whether due to no network access in the build environment, a firewall blocking the specific registry's domain, or a misconfigured proxy, produces failures that look like dependency problems but are actually connectivity problems:

docker build --network=host -t my-api .
npm ERR! network request to https://registry.npmjs.org failed

Testing basic connectivity directly from inside a build-stage container, by running an interactive container from the same base image and attempting to reach the registry manually, isolates whether the problem is genuinely network-related before investigating package-specific causes:

docker run --rm node:20 sh -c "curl -I https://registry.npmjs.org"

Version pinning and lockfile mismatches

A dependency installation that succeeds locally but fails during a Docker build can indicate a lockfile that is out of sync with the manifest file it is supposed to correspond to, which many package managers will reject explicitly when using a strict, lockfile-respecting install command:

npm ci
npm ERR! npm ci can only install packages when your package.json and package-lock.json are in sync

npm ci (and equivalents in other ecosystems) deliberately fails rather than silently resolving a mismatch the way a more permissive install command might, which is generally the correct, safer behavior for a reproducible build, but does require the lockfile to actually be regenerated and committed whenever the manifest changes.

Private registry authentication

Installing a dependency from a private registry requires credentials to be available during the build, and a build environment without those credentials correctly configured fails with an authentication error that can look superficially similar to a missing package error:

npm ERR! 404 Not Found - GET https://registry.example.com/@myorg/private-package
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

Using a BuildKit secret mount to provide registry credentials only for the duration of the specific install step, rather than embedding them directly in the Dockerfile or an ARG, avoids leaving the credential permanently recoverable from the image's layer history.

Platform and architecture mismatches in native dependencies

A dependency with native, compiled components can fail to install or, more subtly, install successfully but fail at runtime if the build and target architectures differ, which is increasingly relevant given the prevalence of both x86 and ARM-based build and deployment infrastructure:

docker build --platform=linux/amd64 -t my-api .
Error: Cannot find module './build/Release/binding.node'

Explicitly specifying the target platform during the build, matching whatever architecture the image will actually run on, avoids a class of dependency failure that can otherwise only manifest after deployment, well after the build itself appeared to succeed without any error.

Stale lockfile or cache producing inconsistent results

A dependency installation that behaves differently between builds, succeeding sometimes and failing others with no underlying code change, can be caused by an unpinned dependency version resolving to a different, newer release between builds, or by relying on a cached layer that no longer reflects the current state of the lockfile:

docker build --no-cache -t my-api .

Building without cache rules out stale layer caching as a contributing factor; a true version-pinning gap, where a dependency specification allows a range of versions rather than an exact one, requires reviewing and tightening the actual version constraints in the manifest file itself.

Disk space exhaustion during installation

A dependency installation that fails partway through with an unclear error, particularly for installs involving large packages or many dependencies, can sometimes be attributable to the build environment running out of disk space rather than any issue with the dependencies themselves:

docker system df
df -h

Checking available disk space on the build host directly, especially in a CI environment that may have accumulated unused images, build cache, or other artifacts over time, rules out or confirms this less obvious but real possibility.

Isolating the failure with an interactive session

For a dependency failure that remains unclear from the build output alone, starting an interactive container from the last successfully cached layer and running the installation command manually, with the ability to investigate further, is frequently the fastest path to a clear answer:

docker build --target deps -t my-api:debug .
docker run -it --rm my-api:debug sh
npm ci --verbose

Running the install command with increased verbosity inside this interactive session often surfaces detail that the default, more condensed build output omits.

Common mistakes

  • Assuming a minimal base image includes compilers and system libraries that a dependency with native compilation needs, without checking and installing them explicitly.
  • Mistaking a network or registry connectivity failure for a dependency-specific problem, without first testing basic connectivity directly from within the build environment.
  • Using a permissive install command that silently resolves a lockfile-manifest mismatch rather than a strict, lockfile-respecting one that fails explicitly and clearly when they diverge.
  • Embedding private registry credentials directly in the Dockerfile or as a plain ARG instead of using a BuildKit secret mount scoped to only the relevant install step.
  • Building without specifying a target platform when the build and deployment environments use different CPU architectures, risking a native dependency failure that only appears after deployment.

Dependency install failures are almost always explainable by one of a small set of recognizable causes, and reading the actual error message carefully, rather than assuming a generic "dependency problem," combined with isolating the failure interactively when the cause is not immediately clear from the build output alone, resolves the large majority of cases efficiently.

Content in this section