2

Despite there are a lot of Q/A on internet I couldn't find anything that would fix me the issue that running a simple express js app, won't refresh on changes when running it inside Docker.

Here's my Dockerfile

FROM node:16-alpine3.11
WORKDIR /var/be_core
VOLUME /var/be_core
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]

The docker-compose.yml

version: '3.9'
volumes:
  be_core_volume:

services:
  be_server_core:
    ports:
      - "9000:3000"
    image: jeko/be_server_core
    environment:
      - BE_SERVER_CORE_PORT=3000
    volumes:
      - "be_core_volume:/var/be_core"

The js server, really simple:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.send("Hello world");
});

const PORT = process.env.BE_SERVER_CORE_PORT || 3000;

app.listen(PORT, () => {
    console.log(`Core server running on internal port : ${PORT}`);
});

the command npm start simply run that

"scripts": {
        "start": "nodemon -L ./server.js"
}

If I run the server as a stand alone process (not within a docker container) it works perfectly. using the command docker-compose up - instead - seems to works fine, since it starts everything, but when changing something in the server.js file, it doesn't refresh.

What am I doing wrong?

Jacopo Sciampi
  • 2,990
  • 1
  • 20
  • 44

1 Answers1

1

You're overwriting your application code with a Docker named volume.

volumes: can mount two things, either a directory from the host system (a bind mount) or storage managed by Docker (a named volume). When the part on the left-hand side of the colon is just a name, it's mounting a named volume.

Named volumes have some odd behavior when you mount a volume over a directory that has content in the image. The first time only when you run the container, the contents of the image are copied to the volume. Every time after that, though, the (old) contents of the volume take precedence, even if the underlying image is updated. So with your setup not only is it not mounting the host directory (since it's a named volume) but it's also ignoring changes to the images if you docker-compose build again.

You can change this volumes: to a bind mount, and the contents of your host system will overwrite the contents of the image. Be careful to only mount your application source code and not your node_modules directory.

version: '3.8'
# no top-level volumes:
services:
  be_server_core:
    ...
    volumes:
      - "./src:/var/be_core/src" # note path on left-hand-side

Rather than trying to convince Docker to simulate a local development environment, you also might find it more convenient to use an actual local development environment for day-to-day work. Run Node and Nodemon directly on your host as normal, and bring in Docker only for your service's dependencies, and then for integration testing and deployment. You wouldn't have a volumes: block at all here, your entire application would be built into the image.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • 2
    nodemon --legacy-watch – Nay Oo Kyaw Mar 18 '22 at 16:14
  • nodemon --legacy-watch Use this comment – Nay Oo Kyaw Mar 18 '22 at 16:14
  • 1
    https://stackoverflow.com/questions/27226653/nodemon-is-not-working-in-docker-environment – Nay Oo Kyaw Mar 18 '22 at 16:15
  • A Docker container will normally run the fixed copy of the application code that's built into the image, and `nodemon` is ineffective in this environment. To reiterate my last paragraph, the volume setup to make Docker simulate a local development environment is tricky and has often-overlooked details, and if you need `nodemon`, it will work better in a local non-Docker environment. – David Maze Mar 18 '22 at 16:18