Graceful Shutdown Explained — Complete Beginner's Guide
In this tutorial, you will learn about Graceful Shutdown Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Graceful shutdown is the Process of stopping an application by completing in-flight requests, closing connections, releasing resources, and notifying load balancers before the process exits.
What You'll Learn
By the end of this tutorial, you will understand what graceful shutdown is, why it prevents data loss and user-facing errors, and how it differs from killing a process immediately.
Why It Matters
Without graceful shutdown, every deployment or scale-down event drops active requests, corrupts data, and creates a poor user experience. Graceful shutdown is essential for zero-downtime operations.
Real-World Use
DodaZIP's file processing service handles 500 concurrent uploads. When a new version deploys, the old process stops accepting new connections, completes the 500 active uploads, then exits cleanly. Users never see an error.
Graceful Shutdown Learning Path
flowchart LR
A[Circuit Breaker Project] --> B[Graceful Shutdown]
B --> C[Signals]
B --> D[Draining]
B --> E[Kubernetes]
B --> F{You Are Here}
style F fill:#f90,color:#fff
What Happens Without Graceful Shutdown
When a process receives a termination signal, the default behavior is immediate exit. This leaves connections open, requests incomplete, and data in an inconsistent state.
const http = require("http");
const server = http.createServer((req, res) => {
setTimeout(() => {
res.end("Processed request");
}, 5000);
});
server.listen(3000, () => {
console.log("Server running on port 3000");
});
// Kill this process with SIGTERM while a request is in flight
// The request is interrupted, client gets ECONNRESET
What Graceful Shutdown Looks Like
A graceful shutdown sequence typically follows these steps:
const http = require("http");
const server = http.createServer((req, res) => {
setTimeout(() => {
res.end("Processed request");
}, 5000);
});
function gracefulShutdown(signal) {
console.log(`Received ${signal}, starting graceful shutdown`);
server.close(() => {
console.log("All connections closed, exiting");
process.exit(0);
});
setTimeout(() => {
console.error("Forced exit after timeout");
process.exit(1);
}, 10000).unref();
}
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
server.listen(3000, () => {
console.log("Server running, ready for graceful shutdown");
});
// Received SIGTERM, starting graceful shutdown
// All connections closed, exiting
The Graceful Shutdown Sequence
The standard graceful shutdown sequence has six phases:
flowchart LR A[Receive Signal] --> B[Stop Accepting Requests] B --> C[Drain Active Connections] C --> D[Close Database Pools] D --> E[Close Message Queues] E --> F[Exit Process]
Common Mistakes
Calling process.exit() immediately -- process.exit() terminates the process without waiting for pending work. Always give the process time to clean up.
Not setting a shutdown timeout -- If cleanup takes too long, the process must eventually exit. Set a maximum shutdown time of 10-30 seconds and force exit after that.
Ignoring SIGTERM and only handling SIGINT -- Production orchestrators like Kubernetes send SIGTERM, not SIGINT. Handle both signals.
Blocking the event loop during shutdown -- Don't use synchronous operations or long-running computations in the shutdown handler. The event loop needs to run to process remaining requests.
Not notifying the load balancer -- In Kubernetes, remove the pod from service endpoints before draining. Without this step, new traffic arrives during shutdown.
Practice Questions
What is the first step of a graceful shutdown? Stop accepting new requests. The server should close its listening socket or mark itself unhealthy on the health check endpoint.
What signal does Kubernetes send to terminate a pod? SIGTERM. If the process doesn't exit within the terminationGracePeriodSeconds (default 30s), Kubernetes sends SIGKILL.
Why should you set a forced exit timeout? To prevent the process from hanging indefinitely during shutdown due to a stuck connection or resource leak.
Challenge: Write a server that logs the timestamp of every connected client and ensures all active requests complete before exiting.
const http = require("http");
const server = http.createServer((req, res) => {
console.log("Request started at:", new Date().toISOString());
setTimeout(() => {
res.end("Done");
console.log("Request completed at:", new Date().toISOString());
}, 3000);
});
function shutdown() {
console.log("Shutting down at:", new Date().toISOString());
server.close(() => process.exit(0));
}
process.on("SIGTERM", shutdown);
server.listen(3000);
FAQ
Mini Project
Build a simple HTTP server that demonstrates graceful shutdown by logging every request, completing in-flight requests when a signal is received, and rejecting new requests with a proper status code.
const http = require("http");
let shuttingDown = false;
const server = http.createServer((req, res) => {
if (shuttingDown) {
res.writeHead(503, { "Content-Type": "text/plain" });
res.end("Server shutting down");
return;
}
const id = Date.now();
console.log(`[${id}] Request started`);
setTimeout(() => {
res.end(`Request ${id} complete`);
console.log(`[${id}] Request completed`);
}, 2000);
});
function startShutdown() {
shuttingDown = true;
console.log("Health check set to unhealthy");
server.close(() => {
console.log("Server closed, all requests complete");
process.exit(0);
});
setTimeout(() => {
console.error("Shutdown timeout, forcing exit");
process.exit(1);
}, 10000);
}
process.on("SIGTERM", startShutdown);
server.listen(3000, () => console.log("Listening on port 3000"));
What's Next
Now that you understand what graceful shutdown is, learn why graceful shutdown matters for production applications. Then explore how to handle SIGTERM and SIGINT signals correctly.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro