DEV Community

Md Azharuddin
Md Azharuddin

Posted on

Using docker compose watch with Node.js

I was recently working on a small side project when I hit a roadblock. I wanted to use a package but was getting some error while installing in my Windows system. So, I decided to use docker as a development environment. Now docker can be really great for this task and if you use volume and bind mounts you can also set up a reload on save pretty easily.

When I was about to do the setup, I remembered that with Docker Compose version 2.22.0, docker compose watch was released and as per this docs page:

Use watch to automatically update and preview your running Compose services as you edit and save your code.

This seemed like the perfect opportunity to try this!
Furthermore, with the release of Node v22, --watch mode was no more an experimental feature, so another cool thing to include ๐Ÿ˜Š

You can check the whole code in this github repo.

Setup

Install Docker

Since I am using windows, I use Docker Desktop which you can install following this guide: Install Docker Desktop on Windows
NOTE: To install Docker Desktop in Windows, you must install WSL2 first for which you may follow this guide: How to install Linux on Windows with WSL

If you use Mac, follow this: Install Docker Desktop on Mac

If you use Linux, you can either install Docker Desktop or you can manually install just Docker Engine and Docker Compose. If you want to install Docker Desktop, check this guide: Install Docker Desktop on Linux

NOTE: Docker Desktop in simple terms is basically a VM + Docker Engine + GUI + extra features like Compose, Kubernetes, Credential helper etc.

Write a simple Express server

  1. Create a new folder compose-watch
  2. Go to compose-watch folder and terminal there
  3. Run npm init -y. This will instantiate a new node project
  4. Install express using npm i express
  5. Create a new folder src in the root of your project
  6. Create a new file src/index.js and add the following content:
import express from "express";

const app = express();

app.get("/", (req, res) => {
  res.json({ msg: "Hello World" });
});

app.listen(3000, () => {
  console.log("Server is listening at http://localhost:3000");
});
Enter fullscreen mode Exit fullscreen mode

Now add scripts in package.json and change type to module as we are using ES6 imports:

{
  "name": "compose-watch",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run npm run dev to start the server in watch mode.
Now open any browser and type the url http://localhost:3000 and you will get the Hello World response.
Now try changing the Hello World message in src/index.js and you will notice server restarts and now new value appears after calling the url again!

With this we have a working server. So, it's time to dockerize!

Write the Dockerfile

Create a new file in project root called Dockerfile and add the following content:

FROM node:22-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY ./src /app/src
CMD npm run dev
Enter fullscreen mode Exit fullscreen mode

Write the compose file

Create a new file in project root called compose.yml and add the following content:

services:
  server:
    build:
      context: .
    ports:
      - 3000:3000

    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src

        - action: rebuild
          path: package.json
          target: /app
Enter fullscreen mode Exit fullscreen mode

Time to run!

Now we are ready to test. Run the following command to build the image and start the container in watch mode:

docker compose watch
Enter fullscreen mode Exit fullscreen mode

And that's it!
This will start the server in watch mode. If you make a change in message again, you will be able to see the updated message.

But wait! It seems like watch works only once?
When I try to change the message again, it is not reflecting in the server even though the terminal says

Syncing "server" after changes were detected
Enter fullscreen mode Exit fullscreen mode

I am not sure why exactly this issue happens, but it seems like node native --watch flag does not work properly ๐Ÿ˜ž

Switching to nodemon

  1. Install nodemon using npm i --save-dev nodemon
  2. Change the dev script in package.json to nodemon src/index.js

So, now package.json looks like this:

{
  "name": "compose-watch",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now restart the server using docker compose watch command.
It seems like compose watch works perfectly with nodemon.

If you have used nodemon before with docker volumes, you might know that we need to use polling for nodemon to work properly with docker using the -L flag but we don't need that with compose watch.

So, this is how you can use docker as a development environment (at least for now ๐Ÿ˜…). If any of you know how to fix the issue with --watch flag, do let me know!

Again, whole code is available in github repo.

Thanks for reading!

Top comments (3)

Collapse
 
engpetermwangi profile image
Peter Mwangi

Hi @mdazhar1038 , thanks for sharing! node --watch src/index.js plus action: sync did not work for me too - syncing just once but not on subsequent watched file changes. I found node src/index.js plus action: sync+restart to work just fine as an alternative to nodemon src/index.js plus action: sync.

Collapse
 
moritzraho profile image
Moritz Raho

I think in this case, if you already restart, you don't even need to set --watch

Collapse
 
efleurine profile image
Emmanuel

Thank you for sharing your learning.