Hot Reload Fundamentals
Hot Reload Fundamentals
How Hot Reload Works in Docker
Host filesystem (bind mount)
│
▼
Container watches /app/src for changes
│
▼
File change detected → framework rebuilds → server reloads
│
▼
Browser auto-refreshes
The key is bind mounting your source code into the container, then using the framework's built-in file watcher.
Node.js with Nodemon
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install -g nodemon
RUN npm install
COPY . .
EXPOSE 3000
CMD ["nodemon", "--watch", "src", "src/server.js"]
# docker-compose.dev.yml
services:
api:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- ./src:/app/src # Bind mount source
- /app/node_modules # Keep container's node_modules
ports:
- "3000:3000"
command: nodemon --watch src src/server.js
Python with uvicorn --reload
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0"]
services:
backend:
volumes:
- ./src:/app/src
command: uvicorn main:app --reload --host 0.0.0.0
Go with Air
FROM golang:1.21
RUN go install github.com/air-verse/air@latest
WORKDIR /go/src/app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
CMD ["air"]
services:
api:
volumes:
- ./src:/go/src/app
command: air
Vue/React with Vite
services:
frontend:
build:
context: ./frontend
volumes:
- ./frontend/src:/app/src
- /app/node_modules
ports:
- "5173:5173"
command: npm run dev -- --host
Vite uses WebSockets for HMR (Hot Module Replacement) — changes appear instantly without page reload.
Docker Compose Watch Mode
Docker Compose Watch Mode
Compose Watch (v2.22+) provides automatic file synchronization and container rebuilds.
Configure Watch
services:
api:
build:
context: .
dockerfile: Dockerfile
develop:
watch:
# Sync file changes
- action: sync
path: ./src
target: /app/src
# Rebuild container on config change
- action: rebuild
path: ./package.json
# Sync and exec command
- action: sync+restart
path: ./config
target: /app/config
Usage
# Start watch mode
docker compose watch
# Start in background
docker compose watch &
# Watch outputs:
# [api] Syncing file changes to container...
# [api] Executing command: npm install
# [api] Container restarted
Watch Actions
| Action | Description |
|---|---|
sync |
Copy changed files to container |
rebuild |
Rebuild container from Dockerfile |
sync+restart |
Sync files then restart container |
Complete Example
services:
frontend:
build: ./frontend
develop:
watch:
- action: sync
path: ./frontend/src
target: /app/src
- action: sync+restart
path: ./frontend/public
target: /app/public
api:
build: ./api
develop:
watch:
- action: sync
path: ./api/src
target: /app/src
- action: rebuild
path: ./api/package.json
Performance Tips
services:
api:
volumes:
# Use cached consistency for macOS
- ./src:/app/src:cached
# Use delegated for write-heavy operations
- ./dist:/app/dist:delegated
# Named volume for node_modules (fast, no sync needed)
- node_modules:/app/node_modules
volumes:
node_modules:
File Sync Troubleshooting
File Sync Troubleshooting
Common Issues
1. Changes not detected:
# Check if volume is mounted correctly
docker inspect --format '{{json .Mounts}}' web | python3 -m json.tool
# Test file access inside container
docker exec web ls -la /app/src
# Check file modification time
stat ./src/server.js
2. File permission issues:
# macOS: Files created in container may have wrong UID
# Fix: Run container as host user
docker run --user $(id -u):$(id -g) myapp
# Linux: Files may be owned by root
# Fix: Change ownership on host
sudo chown -R $(id -u):$(id -g) ./src
3. macOS performance:
# Slow: Using bind mount through VM
v .:/app
# Faster: Named volume for heavy I/O
v node_modules:/app/node_modules
v .next:/app/.next
# Docker Desktop: Enable "Use VirtioFS" (Settings > General)
# Docker Desktop: Exclude large directories (Settings > Resources > File Sharing)
4. Inotify not working:
# Linux: Check inotify limits
cat /proc/sys/fs/inotify/max_user_watches
# Increase if needed
sudo sysctl fs.inotify.max_user_watches=524288
# Docker: inotify works for bind mounts but NOT for some volume types
5. Multiple watchers competing:
# Don't use both nodemon AND Docker watch
# Choose one:
# Option 1: Nodemon inside container
command: nodemon --watch src src/server.js
# Option 2: Docker Compose watch
# docker compose watch (handles sync and restart)
Debugging Steps
# 1. Verify mount exists
docker inspect web | grep -A 5 Mounts
# 2. Check container's view of files
docker exec web ls -la /app/src
# 3. Modify a file on host and check in container
touch ./src/test.txt
docker exec web ls -la /app/src/test.txt
# 4. Check file events
docker exec web inotifywait -m /app/src &
touch ./src/newfile.js
# Should see CREATE event
# 5. Check container logs for errors
docker logs web --tail 50