Introduction
ts-node-dev is a popular development tool that combines TypeScript compilation with automatic server restart on file changes. It uses file system watchers (fs.watch/fs.watchFile) to detect changes. When watchers fail to detect changes - due to Docker volume mounting, network filesystems, editor save behavior, or watch configuration issues - developers must manually restart the server, slowing development velocity.
Symptoms
- Changing TypeScript files does not trigger server restart
ts-node-devstarts but never restarts after file changes- Works on macOS but fails in Docker or WSL
- Only some files trigger reload, others do not
Watching changes in: ...shows wrong directory
$ ts-node-dev --respawn src/index.ts
[INFO] 10:30:00 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.0)
[INFO] Server running on port 3000
# Edit src/index.ts and save - NO restart
# Edit src/controllers/user.ts and save - NO restart
# Must manually Ctrl+C and restartCommon Causes
- Docker volume mount not propagating file change events to container
--watchdirectory not including all source files- Editor using atomic saves (write to temp file then rename)
- Network filesystem (NFS, SMB) not supporting inotify
ignore-watchpattern excluding files that should trigger reload
Step-by-Step Fix
- 1.Enable polling for Docker and network filesystems:
- 2.```bash
- 3.# Use polling instead of inotify (works with Docker, NFS, WSL)
- 4.ts-node-dev --poll --interval 1000 --respawn src/index.ts
# Or via environment variable POLLING=1 ts-node-dev --respawn src/index.ts ```
- 1.Configure watch directories explicitly:
- 2.```bash
- 3.# Watch specific directories
- 4.ts-node-dev --watch src --watch config --respawn src/index.ts
# Ignore node_modules and dist (default, but may need adjustment) ts-node-dev --ignore-watch "node_modules dist .git" --respawn src/index.ts
# Via package.json "scripts": { "dev": "ts-node-dev --respawn --poll --watch src --ignore-watch 'node_modules dist' src/index.ts" } ```
- 1.Fix Docker volume mount for file watching:
- 2.```yaml
- 3.# docker-compose.yml
- 4.services:
- 5.app:
- 6.build: .
- 7.volumes:
- 8.- ./src:/app/src
- 9.- /app/node_modules # Don't mount node_modules
- 10.# Add polling for file watching in container
- 11.environment:
- 12.- CHOKIDAR_USEPOLLING=true
- 13.- TS_NODE_DEV_POLL=true
- 14.
` - 15.Switch to nodemon with ts-node as alternative:
- 16.```bash
- 17.npm install --save-dev nodemon
# nodemon.json { "watch": ["src"], "ext": ".ts,.js,.json", "ignore": ["src/**/*.spec.ts", "dist"], "exec": "ts-node --transpile-only src/index.ts" }
# Run nodemon ```
- 1.Use tsx for faster TypeScript execution:
- 2.```bash
- 3.npm install --save-dev tsx
# tsx has built-in watch mode and is faster than ts-node-dev npx tsx watch src/index.ts
# In package.json "scripts": { "dev": "tsx watch src/index.ts" } ```
Prevention
- Use
--pollflag when running in Docker or on network filesystems - Keep watch directory focused on
src/only - Use
tsx watchas a modern, faster alternative to ts-node-dev - Configure editor to use safe-write (atomic saves) consistently
- In Docker, use
:delegatedvolume mount option on macOS: - ```yaml
- volumes:
- - ./src:/app/src:delegated
`- Verify file watching works with a test change:
- ```bash
- # In one terminal
- ts-node-dev --respawn src/index.ts
# In another terminal touch src/index.ts # Should trigger restart ```