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-dev starts 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
bash
$ 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 restart

Common Causes

  • Docker volume mount not propagating file change events to container
  • --watch directory not including all source files
  • Editor using atomic saves (write to temp file then rename)
  • Network filesystem (NFS, SMB) not supporting inotify
  • ignore-watch pattern excluding files that should trigger reload

Step-by-Step Fix

  1. 1.Enable polling for Docker and network filesystems:
  2. 2.```bash
  3. 3.# Use polling instead of inotify (works with Docker, NFS, WSL)
  4. 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. 1.Configure watch directories explicitly:
  2. 2.```bash
  3. 3.# Watch specific directories
  4. 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. 1.Fix Docker volume mount for file watching:
  2. 2.```yaml
  3. 3.# docker-compose.yml
  4. 4.services:
  5. 5.app:
  6. 6.build: .
  7. 7.volumes:
  8. 8.- ./src:/app/src
  9. 9.- /app/node_modules # Don't mount node_modules
  10. 10.# Add polling for file watching in container
  11. 11.environment:
  12. 12.- CHOKIDAR_USEPOLLING=true
  13. 13.- TS_NODE_DEV_POLL=true
  14. 14.`
  15. 15.Switch to nodemon with ts-node as alternative:
  16. 16.```bash
  17. 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. 1.Use tsx for faster TypeScript execution:
  2. 2.```bash
  3. 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 --poll flag when running in Docker or on network filesystems
  • Keep watch directory focused on src/ only
  • Use tsx watch as a modern, faster alternative to ts-node-dev
  • Configure editor to use safe-write (atomic saves) consistently
  • In Docker, use :delegated volume 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 ```