Bind Mounts got a live edit loop by bind-mounting the source and running node --watch, at the cost of running a different container from the one you ship. Compose 514 Watch, generally available since Compose 2.22 (September 2023), keeps the real image and pushes changes into it. Each rule under develop.watch maps a host path to an action: sync copies the file into the running container, sync+restart also restarts it, sync+exec runs a command after copying and restart only restarts (both Compose 2.32), and rebuild builds a new image and replaces the container. BookNest's API loads app.js once at startup, and a changed lockfile means new dependencies:
develop:
watch:
- action: sync+restart
path: ./app.js
target: /app/app.js
- action: rebuild
path: package-lock.jsondocker compose up --watch starts and watches in one terminal; watch --no-up attaches to a running stack:
docker compose watch --no-up > ../watch.log 2>&1 & sleep 3
sed -i "s/{ status: 'ok' }/{ status: 'ok', watched: true }/" app.js
sleep 6; curl -s localhost:33000/health; echo
cat ../watch.log; kill %1; git checkout -q app.js{"status":"ok","watched":true}
Watch enabled
Syncing service "api" after 1 changes were detected
Container l3-booknest-api-1 Restarting
Container l3-booknest-api-1 Started
service(s) ["api"] restartedWatch applies the .dockerignore rules, so node_modules and .env never travel, and ignores unmatched paths: an edit to server.js waits for the next build. Compose 2.24 dropped the old rule that the container's user must be able to write the target; here the root-owned /app/app.js was replaced in a container running as UID 1000.