Docker-node: Can't monitor file changes, so how to properly use this image for development?

Created on 6 May 2018  路  10Comments  路  Source: nodejs/docker-node

Hello, it's the third day I'm trying to deal with nodejs and docker for development and production, but there's no way I can make it work. the Dockerizing a Node.js web app guide says "_The guide is intended for development_" which, in my opinion, is not true, since you can't setup a change monitor for your app's source.

I wrote this simple Dockerfile

FROM node:carbon

WORKDIR /usr/src/app

COPY package*.json ./

ENV PATH=$PATH:/usr/src/app/node_modules/.bin
RUN npm install

# Bundle app source
COPY . .

EXPOSE 3333

USER node
CMD ["node", "server.js"]

Works well with docker-compose.yml

version: '3'
services:
  api:
    build: ./api
    ports:
      - 3333
    volumes:
      - ./api:/usr/src/app
      - /usr/src/app/node_modules
    command: nodemon -e js,json server.js

But there's no way I can make local changes reflect in the container. docker logs docker_api_1 display a successful init, but no changes watched.
The only way I could make it work, was by installing nodemon package (which doesn't work either, but it has something like a "per-file legacy polling" technique that actually works), but that consumes a lot of memory and feels really slow. Maybe someone here can give a hint about this issue and we can add it to that guide, or any README because, again, in my opinion, server development without anything like a "livereload" feature is painful. Unless there's some better workflow which I'm not aware of? Thanks in advace.

Docker version 17.10.0-ce, build f4ffd25 (win64)

EDIT: Just found this SO question and looks like that's the only solution. The question is quite old though, so I wonder if we can figure out a better way to bind mount with file change events. Probably any script I can run in the host, parallel to docker?

Most helpful comment

@Frondor we faced a similar issue with trying to use a Docker container for our product in production as well as development and here's the solution we've used.

  1. in our docker-compose config we override the command with tail -f /dev/null to keep the container alive, but not actually run the node server. There are several ways of doing this, mind (this is just one).
  2. we mount the application source from the host to the container (as you have done)
  3. we have a custom monitoring script that runs on the host which does two things:
    3.a monitor for file changes which require a server restart
    3.b monitor for file changes which just need to be recompiled

In the case of 3.a, what we actually do is docker exec into the running container, kill the node process if it exists, and then run it again. So instead of restarting the container, we restart the node process in the container which is near instantaneous.

All 10 comments

In development, you should volume mount your files instead of copying so that your image will always have the current file.

I haven't used compose in a while but I think you can have a dev compose file which use the same config and image but mounts the app instead.

Hi @LaurentGoderre, Am I not doing that in that docker-compose file?

          volumes:
                - ./api:/usr/src/app

considering the folder structure is

/api
    - Dockerfile
    - server.js
/docker-compose.yml

You might be deasling with permission issues

Can you try this dockerfile:

FROM node:carbon

USER node

ENV PATH=$PATH:/home/node/app/node_modules/.bin

WORKDIR /usr/src/app

COPY package*.json ./

# Bundle app source
COPY . .

RUN npm install

EXPOSE 3333

CMD ["node", "server.js"]

I don't think this has something to do with permissions.
I've followed the best practices guide where it clearly states

# At the end, set the user to use when running this image
USER node

If I try what you suggest, then I get this build error:

Step 6/9 : RUN npm install --silent
 ---> Running in b0587ddac9c0
ERROR: Service 'api' failed to build: The command '/bin/sh -c npm install --sile
nt' returned a non-zero code: 243

Placing the USER at the end causes the npm install to run as root which is definitely not recommended.

I realized I made a mistake in my dockerfile

FROM node:carbon

USER node

ENV PATH=$PATH:/home/node/app/node_modules/.bin

WORKDIR /home/node/app

COPY package*.json ./

# Bundle app source
COPY . .

RUN npm install

EXPOSE 3333

CMD ["node", "server.js"]

What's the difference between WORKDIR /home/node/app and /usr/src/app?
In the nodejs website, the docker guide uses /usr folder and also that's the convention adopted in this issue: https://github.com/nodejs/docker-node/issues/96#issuecomment-182038505, but best practices guide says /home/node/app...
Anyway, I can't execute npm install as node user in node:carbon image

Step 6/9 : RUN npm install
 ---> Running in b41cf639d3f0
npm WARN checkPermissions Missing write access to /home/node/app
npm ERR! path /home/node/app
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access
npm ERR! Error: EACCES: permission denied, access '/home/node/app'
npm ERR!  { Error: EACCES: permission denied, access '/home/node/app'
npm ERR!   stack: 'Error: EACCES: permission denied, access \'/home/node/app\'',

npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/home/node/app' }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/node/.npm/_logs/2018-05-08T03_22_50_767Z-debug.log
ERROR: Service 'api' failed to build: The command '/bin/sh -c npm install' retur
ned a non-zero code: 243

I suppose it doesn't make much difference but the USER directive should come before npm install

@LaurentGoderre actually, I found the reason why your suggestion wasn't working, directories created by WORKDIR are owned by root, no matter which USER you're setting before https://github.com/moby/moby/issues/36677

...
# Use node user from carbon image
USER node

# Create app directory
WORKDIR /home/node/app

Fails because WORKDIR directive doesn't take node user in mind to create the nonexistent directory. So I fixed it with

# Use node user from carbon image
USER node

RUN mkdir -p /home/node/app

# Create app directory
WORKDIR /home/node/app

But that creates an unnecessary (and somehow redundant) layer to the image. There should be a way I could make WORKDIR work with node user

@Frondor we faced a similar issue with trying to use a Docker container for our product in production as well as development and here's the solution we've used.

  1. in our docker-compose config we override the command with tail -f /dev/null to keep the container alive, but not actually run the node server. There are several ways of doing this, mind (this is just one).
  2. we mount the application source from the host to the container (as you have done)
  3. we have a custom monitoring script that runs on the host which does two things:
    3.a monitor for file changes which require a server restart
    3.b monitor for file changes which just need to be recompiled

In the case of 3.a, what we actually do is docker exec into the running container, kill the node process if it exists, and then run it again. So instead of restarting the container, we restart the node process in the container which is near instantaneous.

thanks @Frondor . this answer worked like champ.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eyaylagul picture eyaylagul  路  3Comments

frankbaele picture frankbaele  路  3Comments

retrohacker picture retrohacker  路  3Comments

austinfrey picture austinfrey  路  3Comments

kmleow picture kmleow  路  5Comments