Skip to content

Graceful Shutdown Explained — Complete Beginner's Guide

DodaTech Updated 2026-06-28 5 min read

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

  1. Calling process.exit() immediately -- process.exit() terminates the process without waiting for pending work. Always give the process time to clean up.

  2. 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.

  3. Ignoring SIGTERM and only handling SIGINT -- Production orchestrators like Kubernetes send SIGTERM, not SIGINT. Handle both signals.

  4. 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.

  5. 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

  1. 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.

  2. What signal does Kubernetes send to terminate a pod? SIGTERM. If the process doesn't exit within the terminationGracePeriodSeconds (default 30s), Kubernetes sends SIGKILL.

  3. 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.

  4. 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

What is the difference between SIGTERM and SIGKILL?

SIGTERM (signal 15) asks the process to terminate gracefully. SIGKILL (signal 9) immediately terminates the process without any cleanup. SIGKILL cannot be caught or ignored.

How long should the shutdown timeout be?

Typically 10-30 seconds. This is enough time to drain most connections. Kubernetes default terminationGracePeriodSeconds is 30 seconds.

What happens to requests that arrive while shutting down?

They should be rejected immediately with a 503 Service Unavailable status. The health check should return unhealthy before shutdown starts.

Can I reuse the same server after a graceful shutdown?

No. Once closed, an HTTP server cannot be restarted. Create a new server instance if needed.

Does graceful shutdown work with cluster/worker processes?

Yes, but each worker must handle its own shutdown. The primary process should wait for all workers to finish before exiting.

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