Why NestJS Hot Reload Does Not Work in Docker and How to Fix It Properly
Source: Dev.to
The core misunderstanding
Docker does not watch your files. It only sees what exists inside the container. Whether changes are reflected depends entirely on:
- How files get into the container
- How NestJS is started
- Whether filesystem events propagate correctly
Mixing development‑mode expectations with production‑mode setups will prevent hot reload from working.
How NestJS hot reload works without Docker
When you run NestJS locally with:
npm run start:dev
the Nest CLI uses a file watcher. On file change the process restarts automatically.
- No Webpack involved.
- No HMR (Hot Module Replacement).
- Just a process restart, powered by native filesystem events.
What changes when you introduce Docker
Inside a container, file watching depends on three things:
- File injection – are you mounting the source code or copying a snapshot?
- Startup command – are you running Nest in watch mode?
- Event propagation – do filesystem events reach the container?
If any of these is mis‑configured, hot reload breaks.
The only setup that works reliably for development
All of the following must be true:
- Source code is mounted via a volume
- NestJS runs in watch mode (
start:dev) - File watching works inside Docker
Volumes are non‑negotiable
A Dockerfile that simply copies the code:
COPY . .
creates a static snapshot—perfect for production, but useless for development because Docker never sees local edits.
Instead, use a docker‑compose.yml (or docker run) that mounts the project directory:
services:
app:
build: .
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules # exclude host node_modules
The line .:/usr/src/app is the backbone of hot reload.
Excluding node_modules
Mounting the host’s node_modules can cause native‑module mismatches. Excluding it (as shown above) ensures the container uses its own dependencies.
Run Nest in watch mode
npm run start:dev # ✅ correct
# ❌ wrong alternatives:
node dist/main.js
nest start
Running compiled JavaScript disables the watcher, so hot reload is impossible.
The macOS and Windows file‑watching problem
Docker Desktop runs Linux containers inside a VM. Native filesystem events often do not propagate from macOS/Windows hosts into the container, causing NestJS to miss changes.
Fix: enable polling
Set the following environment variables so Node‑based watchers poll for changes instead of relying on events:
CHOKIDAR_USEPOLLING=true
WATCHPACK_POLLING=true
Add them to your docker‑compose.yml (or Dockerfile) under environment:. Polling is a well‑known, reliable workaround.
The webpack confusion explained
NestJS supports two reload strategies:
- Process restart – the default Nest CLI watcher (used with
start:dev). - Hot Module Replacement (HMR) – requires Webpack.
Webpack is only needed if you deliberately enable HMR. Indicators that HMR is active:
// main.ts
if (module.hot) {
module.hot.accept();
}
// nest-cli.json
{
"compilerOptions": { ... },
"webpack": true // <-- enables HMR
}
If you never intended HMR, you don’t need Webpack at all. Installing it just to “fix” Docker reload issues is usually a red‑herring; the missing piece is often the polling configuration described above.
When Webpack HMR makes sense
- Your application has a long startup time.
- You want instant reload without restarting the Node process.
- You have explicitly enabled HMR in the Nest config.
For most backend APIs, a simple process restart on file change is sufficient and far less complex.
Development vs. production mental model
| Aspect | Development | Production |
|---|---|---|
| Volumes | Use host‑to‑container mounts | No volumes; copy files during build |
| Start command | npm run start:dev (watch mode) | Run compiled JS (node dist/main.js) |
| File watching | Enable polling if on macOS/Windows | Not needed |
| Rebuilds | Never rebuild the image for code changes | Rebuild image on each change |
| Webpack | Optional, only for HMR | Not required |
Mixing these worlds (e.g., using volumes in production or rebuilding images for every change) leads to broken workflows.
Final thoughts
- Docker isn’t broken.
- NestJS isn’t broken.
- Hot reload isn’t magic; it’s just file‑watching.
Understanding how files enter the container and how NestJS watches them makes the behavior predictable. If you find yourself rebuilding Docker images just to see a console‑log update, revisit the volume, start‑command, and polling settings. And if you installed Webpack solely to make file watching work, you probably didn’t need it.
If this article saved you time, it will likely save someone else hours of frustration too.