'Locked package.json files in Docker container using docker-compose

I'm using Docker for Mac and Docker Compose for development of a Node.js application and I'm experiencing an error with the package.json file being locked. The specific error after running npm install --save <package> within a running container is:

npm WARN saveError EBUSY: resource busy or locked, rename
'/Example/package.json.1647356251' -> '/Example/package.json'

The simplified package structure is:

▾ docker/
    Dockerfile
  docker-compose.yaml
  package.json

The Dockerfile contains:

FROM node:9.5
ENV SOURCE_CODE /Example
COPY package.json $SOURCE_CODE/package.json
RUN npm --prefix $SOURCE_CODE install $SOURCE_CODE
WORKDIR $SOURCE_CODE

The docker-compose.yaml file contains:

version: "3"

services:
  node:
    build:
      context: ./
      dockerfile: ./docker/Dockerfile
    volumes:
      - ./node_modules/:/Example/node_modules/
      - ./package.json:/Example/package.json

The package.json file contains:

{
  "name": "example",
  "version": "1.0.0",
  "description": "example",
  "license": "UNLICENSED"
}

Running docker-compose run --entrypoint bash node to start bash, then running npm install --save redux inside the container yields the warning about the package.json file being locked, however files are able to be written in the node_modules directory on the host. How can I avoid locks on package.json file using this structure?



Solution 1:[1]

I encountered the same issue and I solved this by mounting the folder where package.json is located instead of package.json itself.

version: "3"

services:
  node:
    build:
      context: ./
      dockerfile: ./docker/Dockerfile
    volumes:
      - .:/Example

Mounting package.json directly as a volume seems to lock the file.

Solution 2:[2]

This is a quite an odd config problem, but I eventually cracked the nut. The idea is to:

  1. Volume mount the host's package-lock.json to package-lock-host.json

  2. Run npm install in the container to generate new contents

  3. Overwrite the host's contents with the container's newly generated contents

docker-compose run \
  --rm \
  --name 'package_lock_json' \
  -v "$(pwd)/package-lock.json":'/package-lock-host.json' \
  --entrypoint /bin/sh \
  'YOUR_CONTAINER_NAME' \
  -c "npm install --package-lock-only && (cat /package-lock.json > /package-lock-host.json)"

Solution 3:[3]

Like Green Tree suggested, playing with links works. The idea:

  1. create an empty folder in the Dockerfile
  2. change the owner
  3. create empty package*.json file(s) in that folder
  4. create links to these files, where you want them in your image
  5. create the equivalent folder in your source, place the package*.json file(s) in there
  6. in docker-compose, link both directories

In you Dockerfile, do something like this:

# Create a home dir, give access to 'node' user
ENV HOME [whatever your home is...]
RUN mkdir -p $HOME && \
    chown node:node $HOME
# Switch to user node
USER node
# We'll place our files in npm-package
# Create that folder, place empty place holders in there,
#   and create a link to them
RUN mkdir $HOME/npm-package && \
    touch $HOME/npm-package/package.json && \
    touch $HOME/npm-package/package-lock.json && \
    ln -s $HOME/npm-package/package.json $HOME/package.json && \
    ln -s $HOME/npm-package/package-lock.json $HOME/package-lock.json

The temp files are there for two reasons:

  • to be able to create a link
  • to have the proper write permissions once they get replace by the mounted versions

Then in docker-compose.yml:

services:
  your-service-name:
    volumes:
      - npm-package/:/opt/app/npm-package/

Works for me (under Linux)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tom Van Rompaey
Solution 2
Solution 3 Will59