Md Azharuddin
Posted on May 12, 2024
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
- Create a new folder
compose-watch
- Go to
compose-watch
folder and terminal there - Run
npm init -y
. This will instantiate a new node project - Install
express
usingnpm i express
- Create a new folder
src
in the root of your project - 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");
});
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"
}
}
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
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
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
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
I am not sure why exactly this issue happens, but it seems like node native --watch
flag does not work properly 😞
Switching to nodemon
- Install nodemon using
npm i --save-dev nodemon
- Change the dev script in
package.json
tonodemon 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"
}
}
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!
Posted on May 12, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.