Node.js cookbook · Intermediate
Shut down a Node.js server gracefully
Deploy without dropping in-flight requests or leaving database connections and jobs half-done.
Approach
On SIGTERM/SIGINT, mark the instance not-ready (readiness probe fails), stop accepting new connections with server.close(), close idle keep-alive sockets, wait for in-flight requests, then close pools, queues and Redis. Force-exit after a timeout shorter than the orchestrator's grace period.
Solution
import http from "node:http";
let shuttingDown = false;
app.get("/readyz", (_req, res) => res.status(shuttingDown ? 503 : 200).json({ ready: !shuttingDown }));
const server = http.createServer(app);
server.keepAliveTimeout = 65_000; // > load balancer idle timeout
server.listen(Number(process.env.PORT ?? 3000));
async function shutdown(signal: string) {
if (shuttingDown) return;
shuttingDown = true;
logger.info({ signal }, "shutting down");
const force = setTimeout(() => { logger.error("forced exit"); process.exit(1); }, 25_000);
force.unref();
await new Promise((r) => setTimeout(r, 5_000)); // let the LB notice readiness=503
server.closeIdleConnections();
await new Promise<void>((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
await Promise.allSettled([worker.close(), pool.end(), redis.quit()]);
logger.info("shutdown complete");
process.exit(0);
}
process.on("SIGTERM", () => void shutdown("SIGTERM"));
process.on("SIGINT", () => void shutdown("SIGINT"));
Pitfalls
- Running Node via
npm startas PID 1 (npm may not forward signals) — runnodedirectly or usetini. - Keep-alive connections preventing
server.close()from finishing. - Exiting before queues/pools flush.
Variations
http-terminatoror@godaddy/terminuspackages implement this pattern.- Fastify:
fastify.close()plusclose-with-grace.
Further reading: Node.js: server.close()
Reference: https://nodejs.org/api/http.html#serverclosecallback
More Node.js recipes
- Add health and readiness endpoints to a Node.js service
- Add JWT authentication to an Express API
- Cache expensive lookups in Redis from Node.js
- Call external APIs with timeouts and retries in Node.js
- Centralize error handling in Express 5
- Hash passwords and implement login safely in Node.js
- Implement cursor pagination with PostgreSQL in Node.js
- Offload CPU-heavy work with worker threads