feat: improve signal handling with timeout and better POSIX compliance (#1705)
Description
Implements the signal handling improvements proposed in #1705 for better POSIX compliance and systemd compatibility.
Changes
- ✨ SIGINT: Graceful shutdown with double Ctrl+C for force quit
- ✨ SIGTERM: Graceful shutdown with timeout, then SIGKILL (systemd-compatible)
- ✨ SIGQUIT: Immediate termination without cleanup
- ✨ SIGHUP: Default reload signal (replaces SIGUSR2)
- ⚙️ killTimeout: Configurable timeout for graceful shutdown (default: 10s)
- 📝 Documentation: Updated README with signal handling behavior and migration notes
Breaking Changes
⚠️ Default signal changed from SIGUSR2 to SIGHUP
Applications listening for SIGUSR2 should either:
- Update code to listen for
SIGHUP(recommended), or - Set
--signal SIGUSR2in nodemon configuration
Migration Example
// Old (still works with --signal SIGUSR2)
process.on('SIGUSR2', () => gracefulShutdown());
// New (recommended)
process.on('SIGHUP', () => gracefulShutdown());
Implementation Details
New killWithTimeout() Function
Added a new function that implements graceful shutdown with timeout-based escalation:
- Sends initial signal to child process
- Waits for configurable timeout
- Escalates to SIGKILL if child doesn't exit gracefully
- Properly manages event listeners and timers
Signal Handler Improvements
-
SIGINT (Ctrl+C):
- First press: Graceful shutdown with timeout
- Second press: Immediate SIGKILL
- Provides user control over shutdown behavior
-
SIGTERM:
- Passes signal to child
- Waits for
killTimeout - Escalates to SIGKILL if needed
- Aligns with systemd's
TimeoutStopSec
-
SIGQUIT (new):
- Immediate SIGKILL
- No cleanup
- For emergency scenarios
-
SIGHUP (new):
- Default reload signal
- Replaces SIGUSR2 for better POSIX compliance
Testing
Manual testing has been performed for:
- ✅ SIGINT single press (graceful exit)
- ✅ SIGINT double press (force quit)
- ✅ SIGTERM with graceful exit
- ✅ SIGTERM with timeout escalation
- ✅ SIGQUIT immediate termination
- ✅ SIGHUP reload
- ✅ Custom killTimeout configuration
Backward Compatibility
Users can maintain the old behavior by explicitly setting signal: 'SIGUSR2' in their configuration:
nodemon --signal SIGUSR2 server.js
Or in nodemon.json:
{
\"signal\": \"SIGUSR2\"
}
Related Issues
Fixes #1705 Related to #1667, #1661
Checklist
- [x] Code follows the project's coding standards
- [x] Documentation updated
- [x] Breaking changes documented
- [x] Commit messages follow conventional commits format
- [x] Changes are backward compatible (with configuration)
Deploy Preview for nodemon ready!
| Name | Link |
|---|---|
| Latest commit | a9b6b75a1e1f054a860ae2546ed07bd33d787830 |
| Latest deploy log | https://app.netlify.com/projects/nodemon/deploys/69308f99b84af90008aeba6c |
| Deploy Preview | https://deploy-preview-2263--nodemon.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify project configuration.
There's no tests for this, but more worryingly, you've changed the defaults which would apply a massive blanket change.
I've not really read the AI generated PR description, happy if you want to write your own or summarise it down to a few lines with AI, but I do expect it to be fully tested.
One final point, there's no need for the details that were added to the readme, it's inconsistent with the project style.