Why does `package-lock.json` causes a failure in a docker container build when `npm install`?
docker, node.js, npm, package-lock.json
Solution
From your question:
Note: npm install works fine on my local machine, just fails in docker container
If you are using `npm install`, you are not sure to have the same version of dependencies.
For having a reproducible environment, without unexpected issues because of different version of dependencies, you'd rather use `npm ci` (clean-install):
This command is similar to npm-install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment – or any situation where you want to make sure you’re doing a clean install of your dependencies. It can be significantly faster than a regular npm install by skipping certain user-oriented features. It is also more strict than a regular install, which can help catch errors or inconsistencies caused by the incrementally-installed local environments of most npm users.
In short, the main differences between using npm install and npm ci are:
- The project must have an existing package-lock.json or npm-shrinkwrap.json.
- If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
- npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
- If a node_modules is already present, it will be automatically removed before npm ci begins its install.
- It will never write to package.json or any of the package-locks: installs are essentially frozen.
A Fabian Gander's article gives further clarification about the `npm install` and `npm ci` tools and provides advice on when to use each one. The below table is from that source:
cases | npm install | npm ci
--------------------------------------|-------------|-------------
needs package.json | no | yes
needs package-lock.json | no | yes
installs from package.json | yes | no
installs from package-lock.json | no | yes
compares both | no | yes
updates loose package versions | yes | no
updates loose dependencies | yes | no
writes to package.json | yes | no
writes to package-lock.json | yes | no
deletes node_modules before install | no | yes
used for installing separate package | yes | no
should be used on build systems / CI | no | yes
can be used for development | yes | yes
reproducible installs | no | yes
This is why package-lock.json is there, to be available for tools like `npm ci`.
After having a reproducible environment, if this doesn't fix your issue, you need to keep investigating, but IMO it should be the first step.
Problem
There are a lot of people online asking this same question in different ways but there is no clear answer. Can anybody understand enough to explain why a `docker build` fails when `package-lock.json` file exists in the application, but runs successfully when it is not? Seemingly it is related to npm but it is not clear. Everybody says delete the `package-lock.json`, but it is there for a reason. Note: `npm install` works fine on my local machine, just fails in docker container. If I have this Dockerfile: ``` # First Stage: Builder FROM node:13.12.0-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build ``` And run this: ``` docker build -t container-tag ./ ``` I get this: ``` npm WARN tar ENOENT: no such file or directory, open '/app/node_modules/.staging/eventsource-c2615740/example/index.html' npm WARN tar ENOENT: no such file or directory, open '/app/node_modules/.staging/eventsource-c2615740/example/sse-client.js' npm WARN tar ENOENT: no such file or directory, open '/app/node_modules/.staging/react-router-a14663ae/README.md' ``` But this Dockerfile will run successfully: ``` # First Stage: Builder FROM node:13.12.0-alpine AS build WORKDIR /app COPY package.json ./ #<-------- note that there is no star here RUN npm install COPY . . RUN npm run build ```