patterntypescriptexpressMajor
Graceful Shutdown: Draining In-Flight Requests
Viewed 0 times
graceful shutdownSIGTERMserver.closedrainKubernetesin-flight requests
node
Problem
Servers killed abruptly (SIGTERM from Kubernetes, deploys) drop in-flight requests, break database transactions, and leave resources uncleaned.
Solution
Listen for SIGTERM, stop accepting new connections, and wait for existing requests to complete.
import http from 'node:http';
const server = http.createServer(app);
server.listen(3000, () => console.log('Server listening'));
const SHUTDOWN_TIMEOUT = 30_000;
async function shutdown(signal: string) {
console.log(`Received ${signal}. Starting graceful shutdown.`);
// Stop accepting new connections
server.close(async (err) => {
if (err) {
console.error('Error closing server', err);
process.exit(1);
}
// Close DB connections, flush queues, etc.
await db.destroy();
await queue.close();
console.log('Graceful shutdown complete');
process.exit(0);
});
// Force exit if drain takes too long
setTimeout(() => {
console.error('Shutdown timeout — forcing exit');
process.exit(1);
}, SHUTDOWN_TIMEOUT).unref();
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));Why
Container orchestrators (Kubernetes) send SIGTERM before SIGKILL, giving the process time to finish work. Without a handler, the process exits immediately, dropping active requests.
Gotchas
- Set the shutdown timeout to less than Kubernetes terminationGracePeriodSeconds (default 30s).
- server.close() stops new connections but does not close existing keep-alive connections — use 'connection: close' header or a library like http-terminator.
- Always handle both SIGTERM (container stop) and SIGINT (Ctrl+C in development).
Revisions (0)
No revisions yet.