2

I am aiming to configure docker so that when I modify a file on the host the change is propagated inside the container file system.

You can think of this as hot reloading for server side node code.

The nodemon file watcher should restart the server in response to file changes.

However these file changes on the host volume don't seem to be reflected inside the container when I inspect the container using docker exec pokerspace_express_1 bash and inspect a modified file the changes are not propagated inside the container from the host.

Dockerfile

FROM node:8

MAINTAINER therewillbecode

# Create app directory
WORKDIR src/app

RUN npm install nodemon -g

# Install app dependencies
COPY package.json .
# For npm@5 or later, copy package-lock.json as well
# COPY package.json package-lock.json ./

RUN npm install


CMD [ "npm", "start" ]

docker-compose.yml

version: '2'
services:
  express:
   build: .
   depends_on:
    - mongo
   environment:
    - MONGO_URL=mongo:27017/test
    - SERVER_PORT=3000
   volumes:
    - ./:/src/app
   ports:
    - '3000:3000'
   links:
    - mongo

  mongo:
    image: mongo
    ports:
     - '27017:27017'

  mongo-seed:
    build: ./mongo-seed
    links:
     - mongo

.dockerignore

.git
.gitignore
README.md
docker-compose.yml

How can I ensure that host volume file changes are reflected in the container?

therewillbecode
  • 7,090
  • 4
  • 35
  • 42
  • Can you show us a specific sequence of steps that reproduces the problem? I'm not able to reproduce this behavior myself. – larsks Sep 04 '17 at 23:18
  • Please include the listing of the file on your host, the exec and listing into the container, and the output of `docker container inspect $container_id -f '{{.Mounts|json}}'` on your container id. – BMitch Sep 04 '17 at 23:27

3 Answers3

5

Try something like this in your Dockerfile:

CMD ["nodemon", "-L"]

Some people had a similar issue and were able to resolve it with passing -L (which means “legacy watch”) to nodemon.

References:

Kayvan Mazaheri
  • 2,447
  • 25
  • 40
2

Right, so with Docker we need to re-build the image or figure out some clever solution.

You probably do not want to rebuild the image every time you make a change to your source code.

Let's figure out a clever solution. Let's generalize the Dockerfile a bit to solve your problem and also help others.

So this is the boilerplate Dockerfile:

FROM node:alpine

WORKDIR '/app'

COPY package.json .
RUN npm install

COPY . .

CMD ["npm", "run", "start"]

Remember, during the image building process we are creating a temporary container. When we make the copies we are essentially taking a snapshot of the contents /src and /public. Its a snapshot that is locked in time and by default will not be updated by making changes to the code.

So in order to get these changes to files /src and /public, we need to abandon doing a straight copy, we are going to adjust the docker run command that we use to start up our container.

We are going to make use of a feature called volume.

With Docker volume we setup a placeholder inside our Docker container, so instead of copying over our entire/src directory we can imagine we are going to put a reference to those files and give us access to the files and folders inside of the local machine.

We are setting up a mapping from a folder inside the container to a folder outside a container. The command to use is a bit painful, but once its documented here you can bookmark this answer.

docker run -p 3000:3000 -v /app/node_modules -v $(pwd):/app <image_id>

-v $(pwd):/app used to set up a volume in present working directory. This is a shortcut. So we are saying get the present working directory, get everything inside of it and map it up to our running container. It's long winded I know.

To implement this you will have to first rebuild your docker image by running:

docker build -f Dockerfile.dev .

Then run:

docker run -p 3000:3000 -v $(pwd):/app <image_id>

Then you are going to very quickly get an error message, the react-scripts not found error. You will see that message because I skipped the -v /app/node_modules.

So what's up with that?

The volume command sets up a mapping and when we do, we are saying take everything inside of our present working directory and map it up to our /appfolder, but the issue is there is no /node_modules folder which is where all our dependencies exist.

So the /node_modules folder got overwritten.

So we are essentially pointing to nothing and thats why we need that -v /app/node_modules with no colon because the colon is to map up the folder inside a container to a folder outside the container. Without the colon we are saying want it to be a placeholder, don't map it up against anything.

Now, go ahead and run: docker run -p 3000:3000 -v $(pwd):/app <image_id>

Once done, you can make all the changes you want to your project and see them "hot reload" in your browser. No need to figure out how to implement Nodemon.

So whats happening there is any changes made to your local file system is getting propagated into your container, the server inside your container sees the change and updates.

Now, I know its hard and annoying to remember such a long command, in enters Docker Compose.

We can make use of Docker Compose to dramatically simplify the command we have to run to start up the container.

So to implement that you create a Docker Compose file and inside of it you will include the port setting and the two volumes that you need.

Inside your root project, make a new file called docker-compose.yml.

Inside there you will add this:

version: '3'
services: 
  web: 
    build: .
    ports:
      - "3000:3000"
    volumes:
      - /app/node_modules
      - .:/app

Then run: docker-compose up

Daniel
  • 14,004
  • 16
  • 96
  • 156
  • Stephen Grider is a real magician. Every bit of his videos gets stuck in the mind. Btw the command is not mapping the changes from the host in Windows. – Neeraj Sewani Jul 10 '19 at 18:40
  • I am still facing this issue in April-2021. HMR doesn't work for ANY front-end, Node back-end and/or Mongo/MySQL database. The system is Windows 10 Home Edition with WSL2. – Santhosh John Apr 18 '21 at 14:12
0

Daniel's answer partially worked for me, but the hot reloading still doesn't work. I'm using a Windows host and had to change his docker-compose.yml to

version: '3'
services: 
  web: 
    build: .
    ports:
      - "3000:3000"
    volumes:
      - /App/node_modules
      - .:/App

(I changed the volumes arguments from /app/node_modules to /App/node_modules and from .:/app to .:/App. This enables changes to be passed to the container, however the hot reloading still doesn't work. I have to use docker-compose up --build each time I want to refresh the app.)

Sam J
  • 9
  • 4
  • I am still facing the same issue as Sam's in April-2021. HMR doesn't work for ANY front-end, Node back-end and/or Mongo/MySQL database. – Santhosh John Apr 18 '21 at 14:11