[Docker Course 4-13] Issue on npm install step

Hello there,

I’ve facing an issue while installing the app dependencies, returning an user permission denied while accessing the /app directory. Can you guys tell what am I missing?

Here is the Dockerfile:

FROM node:14.16.0-alpine3.13

RUN addgroup app && adduser -S -G app app
USER app

WORKDIR /app
COPY . .

RUN npm install


ENV API_URL=http://api.myapp.com/
EXPOSE 3000

and after running docker build -t react-app ., I get the following error:

Step 6/8 : RUN npm install
 ---> Running in 36bd85f20663
npm WARN deprecated babel-eslint@10.1.0: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
npm WARN deprecated querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated rollup-plugin-babel@4.4.0: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-babel.
npm WARN deprecated @hapi/joi@15.1.1: Switch to 'npm install joi'
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated @hapi/address@2.1.4: Moved to 'npm install @sideway/address'
npm WARN deprecated @hapi/topo@3.1.6: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/hoek@8.5.1: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/bourne@1.3.2: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated core-js@2.6.12: core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.
npm WARN checkPermissions Missing write access to /app
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^2.1.3 (node_modules/react-scripts/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules/watchpack-chokidar2/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules/webpack-dev-server/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.14.5 requires a peer of @babel/core@^7.13.0 but none is installed. You must install peer dependencies yourself.
npm WARN tsutils@3.21.0 requires a peer of typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta but none is installed. You must install peer dependencies yourself.

npm ERR! code EACCES
npm ERR! syscall access
npm ERR! path /app
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, access '/app'
npm ERR!  [Error: EACCES: permission denied, access '/app'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/app'
npm ERR! }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! 
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/app/.npm/_logs/2021-07-17T19_24_29_469Z-debug.log
The command '/bin/sh -c npm install' returned a non-zero code: 243

Hi,

here the solution:

  • since app user don’t have permissions on app folder and files that were created by root user,
    you need to give it after created user and before switch to app user

  • here the command : RUN chown -R app /app ,
    where here you are changing the owner of the folder and the files that in the future are created.

here final docker file:


FROM node:14.16.0-alpine3.13

WORKDIR /app/

# chown -R change the owner of app folder to app

# the node_modules will be owned by app too

RUN addgroup app && adduser -S -G app app && chown -R app /app

USER app

# When using COPY with more than one source file, the destination must be a directory and end with a /

COPY package*.json ./

RUN npm i

# When using COPY with more than one source file, the destination must be a directory and end with a /

COPY . ./

ENV API_URL=http://api.app.test

EXPOSE 3000

CMD [ "npm", "start" ]
3 Likes

Hi I have the same problem I try use your Dockerfile but I have next error:
Building backend
Sending build context to Docker daemon 292.9kB
Step 1/10 : FROM node:14.16.0-alpine3.13
—> 50bfd284aa0d
Step 2/10 : WORKDIR /app/
—> Using cache
—> 8287264d9518
Step 3/10 : RUN addgroup app && adduser -S -G app app && chown -R app /app
—> Using cache
—> eec0bd9ac2b4
Step 4/10 : USER app
—> Using cache
—> d7af4c69f930
Step 5/10 : COPY package*.json ./
—> Using cache
—> fb0c7dd10b1e
Step 6/10 : RUN npm i
—> Running in 8c671a881e9b
npm WARN vidly-backend@1.0.0 No description
npm WARN vidly-backend@1.0.0 No repository field.

npm ERR! Maximum call stack size exceeded

npm ERR! A complete log of this run can be found in:
npm ERR! /home/app/.npm/_logs/2021-08-20T04_06_12_916Z-debug.log
The command ‘/bin/sh -c npm i’ returned a non-zero code: 1
ERROR: Service ‘backend’ failed to build : Build failed

I researched and tested for 6 Hours and found that the below SCRIPT 1 as shown in Mosh’s lecture video is working only if you use the same version of node in video.

SCRIPT 1 - From Video

FROM node:14.16.0-alpine3.13

RUN addgroup app && adduser -S -G app app
USER app

WORKDIR /app

RUN npm install
EXPOSE 3000

CMD [“npm”, “start”]

SCRIPT 2 -

Latest and other alpine versions have default use NODE which is overriding our app users so below is the solution to use both NODE user and our app users.

###Using custom user i.e. app###

FROM node:16.10.0-alpine3.14

RUN addgroup app && adduser -S -G app app

USER app

WORKDIR /app

COPY --chown=app:node package*.json ./

RUN npm install

COPY --chown=app:node . .

EXPOSE 3000

CMD [“npm”, “start”]

###Using default user i.e. node###

FROM node:16.10.0-alpine3.14

RUN addgroup app && adduser -S -G app app

USER app

WORKDIR /app

COPY --chown=node:node package*.json ./

RUN npm install

COPY --chown=node:node . .

EXPOSE 3000

CMD [“npm”, “start”]

1 Like

I am also getting this error eventhough i’m using EXACTLY the same versions he uses in his course. Wouldn’t it be a good idea to fix the course instead of digging hyper-deep to try to find a work-around (that i don’t understand) to this problem ?

3 Likes

Thanks for working six hours to fix this. Unfortunately it does not work for me. I thought avoiding this kind of situation was one of the essential points of using Docker in the first place …
This is my error message from RUN npm install:

Error: EACCES: permission denied, mkdir ‘/app/node_modules’

1 Like

@ [jediampm] This actually worked, thank you! I had that idea myself, but did not know about -R, so I failed. Linux is killing me …

EDIT: In fact, not so lucky after all. Trying to run the image, just another error:

Error: EACCES: permission denied, scandir ‘/app/src’

… and no idea where to fix the permissions. Anyone?

Maybe I’ve got it now. I had an error in the run command. The correct one is:

docker run -it react-app sh

EDIT: Sorry, this does not work either. There is not error message with this, but the script won’t compile or run either. Back to square one.

Perhaps an explanation why it works for Mosh in the video from this article: Avoiding Permission Issues With Docker-Created Files · vsupalov.com

if you’re using something like docker on mac, you won’t run into those permission issues, as the file sharing is done through NFS and your local files will have the right user.

And Mosh happens to use a Mac, so he does not see the issue. (Me: Debian on Chromebook)

EDIT2:
Finally got it to run by slightly changing the order of commands. This is my Dockerfile now:

FROM node:14.19.0-alpine3.14

#RUN apk add sudo

WORKDIR /app/

#Does not work:  https://vsupalov.com/docker-shared-permissions/

# When using COPY with more than one source file, the destination must be a directory and end with a /

COPY package*.json ./

RUN npm i

# When using COPY with more than one source file, the destination must be a directory and end with a /

COPY . ./

#https://github.com/mhart/alpine-node/issues/48
#The -S flag will add to system group which is equivalent to -r in Ubuntu.
RUN addgroup -S app  &&  adduser -S -G app app  &&  chown -R app /app

USER app

ENV API_URL=http://api.app.test

EXPOSE 3000

CMD [ "npm", "start" ]

Below is my commented Dockerfile which works, for those who are looking for a solution to the permission problems encountered.

NOTE: My directory structure looks like this:

Parent Root
├── Dockerfile
└── react-app
└── […]

So I’m copying “react-app”. Yours is different if you’re following the video exactly, so your COPY instructions will need to be altered.

# Create a node base image running node 16.14.0 on Alpine linux version 3.15
FROM node:16.14.0-alpine3.15

# As root, create a system-user group and system user
RUN addgroup app && adduser -S -G app app

# As root, create the application directory and reassign to system user and group
RUN mkdir /app && chown app:app /app

# Switch from root user to newly created system user
USER app

# Set working directory to be newly create /app directory, which app user owns
# ..., see note on previous RUN instruction
WORKDIR /app

# Copy and set owner of 3rd party application files
# - The --chown option would be required to allow system user access if /app were
#   ...not already owner, e.g.: COPY --chown=app:app react-app/package*.json
# - Copying only files required for 3rd party installation and then installing
#   ...the 3rd party libraries optimizes docker's image re-build process by 
#   ...separating/isolating these steps in their own layer. Doing so helps 
#   ...better utilize docker's caching mechanisms ("image layers") for rebuilds
COPY react-app/package*.json .

# Install 3rd-party application files
# - See previous note about optimization, caching, and image layers
# - Note: You'd get a permission error on build at this point IF system user (app)
#   ...does not own/have rwx permissions on /app directory.
RUN npm install

# Copy and set owner of application files 
# - The --chown option would be required to allow system user to utilize files if
#   ...system user were not already owner of /app, e.g.: COPY --chown=app:app react-app/
# - See previous note about optimization, caching, and image layers
COPY react-app/ .

# Set environment variables as desired
ENV API_URL=http://api.myapp.com

# Expose container on desired port
EXPOSE 3000

# Execute application start command
# - Prefer the 'execute form' of the CMD directive, ie. CMD ['npm', 'start']
#   ...The execute form will run the command in the current shell.
# - The 'shell form' (CMD npm start) would execute command in new shell, which,
#   ...unless required for some reason, is less efficient and starts new process
CMD ["npm", "start"]

Below is my commented Dockerfile which works, running on MacOS, FWIW.

Note my directory structure doesn’t match video exactly. So you’ll need to modify the COPY directives appropriately. Here’s my directory structure:

Parent Root
├── Dockerfile
└── react-app
└── […]

And here’s my commented Dockerfile:

# Create a node base image running node 16.14.0 on Alpine linux version 3.15
FROM node:16.14.0-alpine3.15

# As root, create a system-user group and system user
RUN addgroup app && adduser -S -G app app

# As root, create the application directory and reassign to system user and group
RUN mkdir /app && chown app:app /app

# Switch from root user to newly created system user
USER app

# Set working directory to be newly create /app directory, which app user owns
# ..., see note on previous RUN instruction
WORKDIR /app

# Copy and set owner of 3rd party application files
# - The --chown option would be required to allow system user access if /app were
#   ...not already owner, e.g.: COPY --chown=app:app react-app/package*.json
# - Copying only files required for 3rd party installation and then installing
#   ...the 3rd party libraries optimizes docker's image re-build process by 
#   ...separating/isolating these steps in their own layer. Doing so helps 
#   ...better utilize docker's caching mechanisms ("image layers") for rebuilds
COPY react-app/package*.json .

# Install 3rd-party application files
# - See previous note about optimization, caching, and image layers
# - Note: You'd get a permission error on build at this point IF system user (app)
#   ...does not own/have rwx permissions on /app directory.
RUN npm install

# Copy and set owner of application files 
# - The --chown option would be required to allow system user to utilize files if
#   ...system user were not already owner of /app, e.g.: COPY --chown=app:app react-app/
# - See previous note about optimization, caching, and image layers
COPY react-app/ .

# Set environment variables as desired
ENV API_URL=http://api.myapp.com

# Expose container on desired port
EXPOSE 3000

# Execute application start command
# - Prefer the 'execute form' of the CMD directive, ie. CMD ['npm', 'start']
#   ...The execute form will run the command in the current shell.
# - The 'shell form' (CMD npm start) would execute command in new shell, which,
#   ...unless required for some reason, is less efficient and starts new process
CMD ["npm", "start"]

Everything worked fine until the 5.11 video. The problem has been started with the $(pwd) command… This command does not exist on Windows. Windows accepts %cd% instead, but that didn’t work either. I found a solution by installing Ubuntu on Windows 11 so I can use pwd. After that, I had to install node separately on Ubuntu as well, so I could create image again.
After thIs there is also a problem with the permission… Without $(pwd), the files are created by the app and the root user so react-app is working. But if we use $(pwd), then all files will be created by node user… So I decided to use node user instead of app user.
Here is my code:

FROM node:14.16.0-alpine3.13
USER node
WORKDIR /app
RUN mkdir data
COPY --chown=node:node package*.json .
RUN npm install
COPY --chown=node:node . .
ENV API_URL=http://api.myapp.com/
EXPOSE 3000
CMD [“npm”, “start”]

It works fine!

@ jediampm

Better to just create the user and switch context in the Dockerfile… saves steps of having to change owner of the directory…

1 Like

I think some of the issues listed here may happen because the Union File System used by Docker is create in layers. So some of these folders were already created using root, then trying to rebuild the Docker with a new user.

One solution is indeed to change the owner of the folder, but this might not be desired depending on how your production app is structured… for example if you get a Docker image from another team at work and they don’t want the Owner to be changed because, let’s say you have to make some modifications and then return the image back to them.

A better solution might be to use a group permission instead of changing owner. For example, if you are running the first example *react-app*, you could create a group (lets say *react-app*)

node:14.16.0-alpine3.13
RUN addgroup react-app && adduser -S -G react-app react-app

then if that group is not the group assigned to the folder, change the group with chgrp (probably want to include -R for recursive, changes all files and folders under that group)

RUN chgrp -R react_app /app/

then modify the group permissions for the folder *chmod 775*, which changes the permissions from drwxr-xr-x to drwxrwxr-x

RUN chmod 775 /app/

THEN run as the specified user *react-app*

USER react-app

This would avoid changing the original owner of the folder…

Here is an example of these steps run on a generic Ubuntu image

MacBook-Pro-548:react-app $ docker run -it ubuntu
root@1ef48c56bfb5:/# addgroup test-grp
Adding group 'test-grp' (GID 1000) ...
Done.
root@1ef48c56bfb5:/# mkdir /tmp/ch_test
root@1ef48c56bfb5:/# ls -la /tmp/ | grep ch_test
drwxr-xr-x 2 root **root** 4096 Jan 17 13:03 ch_test
root@1ef48c56bfb5:/# chgrp test-grp /tmp/ch_test
root@1ef48c56bfb5:/# ls -la /tmp | grep ch_test
drwx**r-x**r-x 2 root **test** 4096 Jan 17 13:03 ch_test
root@1ef48c56bfb5:/# chmod -R 775 /tmp/ch_test/
root@1ef48c56bfb5:/# ls -la /tmp/ | grep ch
drwx**rwx**r-x 2 root **test** 4096 Jan 17 13:03 ch_test

You can see from the above session, the group changes to *test-grp* after running the chgrp command, and the permissions change to *drwx**rwx**r-x* after running the chmod command. This gives the group *test-grp* permissions to write to the */tmp/ch_test* directory.

Another solution… You could also delete all images and start over, creating the folder AFTER creating the user and assigning that user in your Dockerfile.

If you have many images linked to the react-app, because lets say you have rebuilt the image many times changing things in the Dockerfile… you can search using grep, strip out the excess using sed, output that to a file, then write a quick while loop which runs the docker rmi --force on each image…

Example of cluttered Docker image repo

MacBook-Pro-548:section4-react-app $ docker images
REPOSITORY                             TAG                       IMAGE ID       CREATED         SIZE
react-app                              latest                    470c4e1ab075   11 hours ago    297MB
<none>                                 <none>                    b534d33ed999   11 hours ago    298MB
<none>                                 <none>                    56bcd1346ab0   11 hours ago    297MB
<none>                                 <none>                    6db4658a1e81   11 hours ago    297MB
<none>                                 <none>                    6d790d390fe5   11 hours ago    297MB
<none>                                 <none>                    9a6fa3982d6d   11 hours ago    118MB
<none>                                 <none>                    74b05f495c42   11 hours ago    382MB

Filter output to only display *<none>*

MacBook-Pro-548:section4-react-app $ docker images | grep none
<none>                                 <none>                    b534d33ed999   11 hours ago    298MB
<none>                                 <none>                    56bcd1346ab0   11 hours ago    297MB
<none>                                 <none>                    6db4658a1e81   11 hours ago    297MB
<none>                                 <none>                    6d790d390fe5   11 hours ago    297MB
<none>                                 <none>                    9a6fa3982d6d   11 hours ago    118MB
<none>                                 <none>                    74b05f495c42   11 hours ago    382MB

Use sed to remove everything before the image names

MacBook-Pro-548:section4-react-app $ docker images | grep none | sed 's/\<none>.*<none>                    //g'
b534d33ed999   11 hours ago    298MB
56bcd1346ab0   11 hours ago    297MB
6db4658a1e81   11 hours ago    297MB
6d790d390fe5   11 hours ago    297MB
9a6fa3982d6d   11 hours ago    118MB
74b05f495c42   11 hours ago    382MB
34870cf3b371   21 months ago   117MB

Use another sed to remove everything after the image names

Josephs-MacBook-Pro-548:section4-react-app josephanderson$ docker images | grep none | sed 's/\<none>.*<none>                    //g' | sed 's/   .*//g'
b534d33ed999
56bcd1346ab0
6db4658a1e81
6d790d390fe5
9a6fa3982d6d
74b05f495c42
34870cf3b371

Redirect the output to a temp file

MacBook-Pro-548:section4-react-app $ docker images | grep none | sed 's/\<none>.*<none>                    //g' | sed 's/   .*//g' >> ~/tmp/old_images

Then run a while loop to execute *docker rmi --force* on each image

MacBook-Pro-548:section4-react-app $ while read -r line; do docker rmi --force $line; done < ~/tmp/old_images
Deleted: sha256:b534d33ed9994445b5f5b123ad293e3ba95a4b1eaf6793376f2fb8e1a499f92f
Deleted: sha256:56bcd1346ab0f132f7dc1c0986766e90c2f7cdc3e9b4847ec17e8793a9f0aaad
Deleted: sha256:6db4658a1e81e3e2fb5c2a0f19111adba9661aa0cc1a296f72797a2efeec4e7a
Deleted: sha256:6d790d390fe5d9c457a0192f6d295b3ecc162ddbf103cbe443dfbb5437482572
Deleted: sha256:9a6fa3982d6d064d17591e54627d3e7c0fff858271d9a2217e3a8e802f5296a9
Deleted: sha256:74b05f495c42d059ab8e008380d84d4bcbf877209f0681e343aae2c50dc916ed
Deleted: sha256:34870cf3b3717b4e748082818760cf16761ff1ea1c9c8f8dd913fbead565ed85

Clean Docker repo

Josephs-MacBook-Pro-548:section4-react-app josephanderson$ docker images
REPOSITORY                             TAG                       IMAGE ID       CREATED         SIZE
react-app                              latest                    470c4e1ab075   11 hours ago    297MB

NOTE: I WOULD STRONGLY CAUTION, USE THIS WITH CARE… It can cause irreparable damage to your Docker repo, and you might erase things you didn’t mean to. Always triple check, and DO NOT BLAME ME if you misuse the above to damage your production Docker repo.

You could also use the docker container prune to be safe

Hey there,
I had the same problem with the permission in a newer base image. And after reading this block and testing around I adapt the script like in the following picture (same permissions than in the original script, npm install and npm start works)!

Hopefully it helps!