Deploymentcritical

Graceful Shutdown

Graceful shutdown ensures a Node.js process stops cleanly by finishing in-flight requests and releasing resources before exiting, rather than abruptly terminating active connections.

Memory anchor

Graceful shutdown = closing a restaurant properly. SIGTERM is 'last call!' Stop seating new guests (server.close()), let current diners finish their meals (in-flight requests), clean the kitchen (close DB pools), lock the doors (process.exit). Skip any step and you get abandoned plates or a health code violation (SIGKILL).

Expected depth

Listen for SIGTERM (sent by Kubernetes, systemd, PM2 before killing the process). Call server.close() to stop accepting new connections while letting existing ones finish. Set a timeout (e.g., 30s) to force-exit if shutdown hangs. Release database connections, message queue connections, and any other resources. Signal readiness to exit with process.exit(0).

Deep — senior internals

Kubernetes sends SIGTERM to the pod, then waits terminationGracePeriodSeconds (default 30s) before sending SIGKILL. During this window, the pod is still receiving traffic because the endpoints controller may not have removed it from the load balancer yet. The solution: add a pre-stop sleep (5-10s) or wait for readiness probe failure propagation before closing the server. The full sequence: SIGTERM received → stop readiness probe → sleep N seconds → server.close() → drain DB connections → process.exit(0). Keep-alive connections complicate shutdown because server.close() only stops new connections; existing keep-alive connections stay open. Track active connections with a Set and destroy them on shutdown: `res.socket.destroy()` or `req.socket.destroy()` for persistent connections.

🎤Interview-ready answer

Graceful shutdown: (1) Listen SIGTERM, (2) stop readiness probe, (3) wait for load balancer propagation (~5-10s sleep or pre-stop hook), (4) server.close() to stop accepting new connections, (5) await in-flight requests, (6) close DB connection pools, (7) process.exit(0). Keep-alive connections require explicit socket tracking and destruction since server.close() doesn't force-close them.

Common trap

server.close() in Node.js stops accepting NEW connections but doesn't close existing keep-alive HTTP/1.1 connections. A client with a persistent connection remains connected indefinitely, preventing clean shutdown. Production servers must explicitly track and destroy active sockets or set a Connection: close header during the shutdown window.