When Node.js receives SIGTERM with no handler, it exits immediately - a 'drop everything' exit. Promises and callbacks requiring the event loop to continue won't execute. The process terminates before async operations can complete, causing data loss. Network requests mid-flight are killed, database writes are lost, file handles remain open. Solution: Add SIGTERM handler with process.on('SIGTERM', handler) to control shutdown sequence.
Node.js Graceful Shutdown FAQ & Answers
16 expert Node.js Graceful Shutdown answers researched from official documentation. Every answer cites authoritative sources you can verify.
unknown
16 questionsUse process.on('SIGTERM', handler) NOT process.once. The once variant only fires one time and prevents proper cleanup. process.on allows the handler to complete async operations before exit. Pattern: process.on('SIGTERM', async (signal) => { await cleanup(); }). Don't use process.removeListener after - let Node exit naturally. Using once() prevents graceful shutdown patterns from working correctly.
Calling process.exit() immediately terminates the process, killing any async operations mid-flight. Pending database writes are lost, API responses are truncated, cleanup code doesn't run. Instead, let Node exit naturally when the event loop is empty. Pattern: Close server, wait for requests to complete, close DB connections, then do nothing - Node exits automatically. Only use process.exit() as last resort with timeout.
Call server.close() as first step in SIGTERM handler. This stops accepting new connections while allowing existing requests to complete. Pattern: server.close(() => { console.log('Server closed'); }). The callback fires when all connections are closed. Existing requests continue processing. Don't kill connections immediately - that causes client errors. Typical wait time: 10-30 seconds before forcing shutdown.
Use Promise.race to wait for requests with a timeout. Pattern: await Promise.race([waitForInflightRequests(), timeout(30000)]). The waitForInflightRequests function tracks active request count and resolves when zero. Timeout ensures shutdown doesn't hang forever if a request is stuck. Typical timeout: 30 seconds. After timeout, log warning and force exit. Track requests with counter: increment on request start, decrement on finish.
30 seconds is the recommended timeout for graceful shutdown. This allows most requests to complete (typical API calls: 1-5 seconds) while preventing indefinite hangs. Kubernetes default terminationGracePeriodSeconds is 30s, so match that. If your requests take longer, increase to 60s but no more than 120s. After timeout, log active requests and force exit. Pattern: Promise.race([cleanup(), timeout(30000)]). Adjust based on your slowest p99 request duration.
Close database connections AFTER server.close() and AFTER in-flight requests complete. Order: (1) Stop accepting new requests (server.close()), (2) Wait for active requests to finish, (3) Close database connections (await db.close()), (4) Let Node exit naturally. Don't close DB early - active requests will fail. Pattern: await db.close() as final step before natural exit. Most DB clients have close() or end() methods that drain connection pools.
After terminationGracePeriodSeconds (default 30s), Kubernetes sends SIGKILL which immediately kills the process. Cannot be caught or handled. All async operations are terminated mid-flight, causing data loss and client errors. Your graceful shutdown timeout must be LESS than terminationGracePeriodSeconds. Recommended: Set your app timeout to 25s if using default 30s period. Monitor shutdown duration to detect slow shutdowns before they hit SIGKILL.
When Node.js receives SIGTERM with no handler, it exits immediately - a 'drop everything' exit. Promises and callbacks requiring the event loop to continue won't execute. The process terminates before async operations can complete, causing data loss. Network requests mid-flight are killed, database writes are lost, file handles remain open. Solution: Add SIGTERM handler with process.on('SIGTERM', handler) to control shutdown sequence.
Use process.on('SIGTERM', handler) NOT process.once. The once variant only fires one time and prevents proper cleanup. process.on allows the handler to complete async operations before exit. Pattern: process.on('SIGTERM', async (signal) => { await cleanup(); }). Don't use process.removeListener after - let Node exit naturally. Using once() prevents graceful shutdown patterns from working correctly.
Calling process.exit() immediately terminates the process, killing any async operations mid-flight. Pending database writes are lost, API responses are truncated, cleanup code doesn't run. Instead, let Node exit naturally when the event loop is empty. Pattern: Close server, wait for requests to complete, close DB connections, then do nothing - Node exits automatically. Only use process.exit() as last resort with timeout.
Call server.close() as first step in SIGTERM handler. This stops accepting new connections while allowing existing requests to complete. Pattern: server.close(() => { console.log('Server closed'); }). The callback fires when all connections are closed. Existing requests continue processing. Don't kill connections immediately - that causes client errors. Typical wait time: 10-30 seconds before forcing shutdown.
Use Promise.race to wait for requests with a timeout. Pattern: await Promise.race([waitForInflightRequests(), timeout(30000)]). The waitForInflightRequests function tracks active request count and resolves when zero. Timeout ensures shutdown doesn't hang forever if a request is stuck. Typical timeout: 30 seconds. After timeout, log warning and force exit. Track requests with counter: increment on request start, decrement on finish.
30 seconds is the recommended timeout for graceful shutdown. This allows most requests to complete (typical API calls: 1-5 seconds) while preventing indefinite hangs. Kubernetes default terminationGracePeriodSeconds is 30s, so match that. If your requests take longer, increase to 60s but no more than 120s. After timeout, log active requests and force exit. Pattern: Promise.race([cleanup(), timeout(30000)]). Adjust based on your slowest p99 request duration.
Close database connections AFTER server.close() and AFTER in-flight requests complete. Order: (1) Stop accepting new requests (server.close()), (2) Wait for active requests to finish, (3) Close database connections (await db.close()), (4) Let Node exit naturally. Don't close DB early - active requests will fail. Pattern: await db.close() as final step before natural exit. Most DB clients have close() or end() methods that drain connection pools.
After terminationGracePeriodSeconds (default 30s), Kubernetes sends SIGKILL which immediately kills the process. Cannot be caught or handled. All async operations are terminated mid-flight, causing data loss and client errors. Your graceful shutdown timeout must be LESS than terminationGracePeriodSeconds. Recommended: Set your app timeout to 25s if using default 30s period. Monitor shutdown duration to detect slow shutdowns before they hit SIGKILL.