I'm trying to dockerize a pretty old project that uses Node 12, npm 8 and bower.
Since there's no prebuilt image that bundles these three dependencies, I'm building my own image with this Dockerfile
:
FROM node:12
RUN npm install -g npm@8
RUN npm install -g bower
RUN echo "node version: $(node --version)"
RUN echo "npm version: $(npm --version)"
RUN echo "bower version: $(bower --version)"
Here's the docker-compose.yml
(fragment):
version: "3.9"
services:
frontend:
build: ./front-end
working_dir: /home/node/app/
volumes:
- ./front-end:/home/node/app/
entrypoint: [ "/bin/bash", "-c" ]
command:
- |
npm i
bower i --allow-root
npm start
I run it like this:
docker compose build frontend --no-cache --progress=plain
docker compose up --force-recreate frontend
It crashes like this:
my-frontend-1 | npm ERR! path /home/node/app/node_modules/ember-cli-cookie
my-frontend-1 | npm ERR! command failed
my-frontend-1 | npm ERR! command sh -c -- bower install --allow-root
my-frontend-1 | npm ERR! /home/node/app/node_modules/bower/lib/node_modules/configstore/index.js:54
my-frontend-1 | npm ERR! throw err;
my-frontend-1 | npm ERR! ^
my-frontend-1 | npm ERR!
my-frontend-1 | npm ERR! Error: EACCES: permission denied, open '/root/.config/configstore/bower-github.json'
my-frontend-1 | npm ERR! You don't have access to this file.
Here's my reconstruction of events:
bower i
step of docker-compose.yml
that's crashing, it's npm i
. If I comment out npm i
, then the script proceeds further.npm
installs the ember-cli-cookie@0.2.0
package. npm
calls the postinstall script of ember-cli-cookie
. postinstall
script calls bower install --allow-root
. npm
is sh -c -- bower install --allow-root
. EACCES: permission denied, open '/root/.config/configstore/bower-github.json'
.Here are permissions for the inaccessible file:
root@7d24854cf1af:/home/node/app# ls -alhF /root/.config/configstore/
total 20K
drwx------ 1 root root 4.0K Aug 24 20:16 ./
drwx------ 1 root root 4.0K Aug 24 19:55 ../
-rw------- 1 root root 2 Aug 24 20:16 bower-github.json
-rw------- 1 root root 55 Aug 24 19:55 update-notifier-npm.json
whoami
returns root
.
I checked permissions and username by inserting commands into docker-compose.yml
immediately before npm i
.
I don't understand how this can be possible: root is unable to access a file with permissions of -rw------- 1 root root
.
If I try accessing it manually like this (replicating the sh -c
approach):
sh -c -- "nano /root/.config/configstore/bower-github.json"
...I'm able to view the file save changes to it just fine.
I even tried adding RUN chmod -R a+rwx /root/.config/
to the end of my Dockerfile, rebuilding the image and recreating the container — it made no difference!
Why is this happening and how to fix it?
I've found these related questions but their answers did not help me:
EACCES, permission denied when running bower — I don't have a ~/.cache
folder.
bower install fails giving me EACCES: permission denied error — the user inside the container is root
, the $USER
and $GROUP
env vars are empty.
Error: EACCES: permission denied — the ~/.npm-global
approach did not help. whoami
is root
already.
⚠ Adding --unsafe-perm=true --allow-root
to npm i
resolves the issue, npm i
completes successfully, but then npm start
crashes with exactly the same error (except the inaccessible file is /root/.config/configstore/ember-cli.json
), and adding those flags to npm start
does not help.
npm
is running from the user that's invoking it, but from the user who owns current directoy.
Since the project folder is volumes:
-ed into the container, npm
is running with uid
and gid
of the host OS user. Such user does not exist in the container, so it's kinda a ghost. This user has no access to /root/.config/
, thus the failure.
When node_modules/
is on the host OS filesystem and npm
is in a container, there's gonna be a discrepancy in file ownership, preventing you from eg deleing node_modules/
from the host OS.
The graceful solution to this problem is to create a user in the container with uid
and gid
identical to your user in the host OS. Here's how you do it.
Dockerfile
:
FROM node:12
# https://jtreminio.com/blog/running-docker-containers-as-current-host-user/
ARG USER_ID
ARG GROUP_ID
RUN if [ ${USER_ID:-0} -ne 0 ] && [ ${GROUP_ID:-0} -ne 0 ]; then \
userdel -f node &&\
if getent group node ; then groupdel node; fi &&\
groupadd -g ${GROUP_ID} node &&\
useradd -l -u ${USER_ID} -g node node &&\
install -d -m 0755 -o node -g node /home/node &&\
chown --changes --silent --no-dereference --recursive \
--from=33:33 ${USER_ID}:${GROUP_ID} \
/home/node \
;fi
USER node
# https://stackoverflow.com/a/59151128/901944
RUN mkdir ~/.npm-global
RUN npm config set prefix '~/.npm-global'
ENV PATH=/home/node/.npm-global/bin:$PATH
RUN npm install -g npm@8
Build the container with docker
:
USER_ID=$(id -u) GROUP_ID=$(id -g) \
docker image build \
--build-arg USER_ID=$(id -u ${USER}) \
--build-arg GROUP_ID=$(id -g ${USER}) \
-t frontend \
.
docker-compose.yml
(fragment):
services:
php:
build:
context: .
dockerfile: Dockerfile
args:
USER_ID: ${USER_ID:-0}
GROUP_ID: ${GROUP_ID:-0}
Build the container with docker compose
:
USER_ID=$(id -u) GROUP_ID=$(id -g) docker compose build --no-cache fronend
This amazing article describes the problem in detail and explores potential solutions, starting with simpler ones and explaining why they are no good and why the elaborate solution is required.
https://jtreminio.com/blog/running-docker-containers-as-current-host-user/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.