advanced20 min

Graceful Shutdown and Operational Readiness

What actually happens when a real server needs to stop — and why an abrupt, ungraceful shutdown can silently drop in-flight work that a slightly more careful one wouldn't.

What you'll learn

  • Explain what SIGTERM is and why a production process receives it during a normal deployment
  • Implement a graceful shutdown sequence as an explicit state machine
  • Design a basic health-check endpoint and explain what it should and shouldn't check

Prerequisites

Explanation

Every deployment eventually needs to stop an old, running server process to replace it with a new one. The hosting platform doesn't do this violently by default — it sends the process a SIGTERM signal, a polite, standard request meaning "please finish up and exit," giving the process a window of time (commonly around 10-30 seconds, platform-dependent) before it escalates to a forceful, unstoppable SIGKILL that terminates the process immediately with zero opportunity to clean up anything.

An abrupt shutdown with no handling at all can silently drop in-flight work: a request that was halfway through being processed when the process dies gets no response at all — not an error, nothing, just a connection that goes dead from the client's perspective. A database write that was mid-transaction can leave data in an inconsistent state. A graceful shutdown sequence, listening explicitly for SIGTERM, does three things in order: stop accepting new incoming connections (so nothing new starts that won't have time to finish), wait for requests already in progress to actually complete, and only then close remaining resources (database connections, file handles) and exit cleanly. Node's own http.Server provides exactly the primitive for the first two steps: calling server.close() stops accepting new connections immediately while letting existing ones finish naturally, and its callback fires only once every existing connection has actually closed.

A health-check endpoint (GET /health, conventionally) exists so a load balancer or orchestration platform can ask "is this instance actually ready to receive traffic?" before routing real requests to it, and can detect an instance that's become unhealthy after running for a while. A good health check verifies the specific things that would make the server unable to genuinely serve requests (a database connection is actually reachable, not just that the process is running) — but it should stay fast and lightweight, not itself perform expensive work or depend on things unrelated to whether this server can serve its own requests correctly.

Example

A real graceful-shutdown state machine, modeling the exact sequence a production Node server follows on SIGTERM.

function createShutdownManager() {
  let state = "running";
  let inFlightRequests = 0;

  return {
    getState() { return state; },
    startRequest() { inFlightRequests += 1; },
    finishRequest() { inFlightRequests -= 1; },
    beginShutdown() {
      state = "draining"; // stop accepting NEW work, but let existing work finish
    },
    isReadyToExit() {
      return state === "draining" && inFlightRequests === 0;
    },
  };
}

const manager = createShutdownManager();
manager.startRequest();
manager.beginShutdown();
console.log("Ready to exit while a request is in flight?", manager.isReadyToExit()); // false
manager.finishRequest();
console.log("Ready to exit now?", manager.isReadyToExit()); // true

Try it yourself

Start TWO requests before beginning shutdown, finish only one, and check isReadyToExit() -- should still be false.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Using createShutdownManager already defined, simulate three in-flight requests, begin shutdown, finish all three, then confirm isReadyToExit() is true. Also confirm it was false partway through (after finishing only two of three).

Checks: not ready while a request is still in flight · ready once every request has finished

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write a simplified health check: isHealthy(databaseReachable, startupComplete) that returns true only if BOTH databaseReachable and startupComplete are true. Then write healthCheckResponse(isHealthyValue) returning { status: 200, body: { status: 'ok' } } if healthy, or { status: 503, body: { status: 'unavailable' } } if not.

Checks: healthy when both conditions are true · unhealthy when the database is unreachable · returns 503 for an unhealthy result

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Terminating a process immediately on shutdown with no handling at all, silently dropping requests that were still in progress.
  • A health check that only confirms the process is running, without checking whether it can actually reach the dependencies (like a database) it needs to genuinely serve requests.
  • A health check that itself performs slow, expensive work, becoming a performance problem for whatever's calling it frequently.

Knowledge check

Knowledge check

1. What does SIGTERM represent, and why does a production process typically receive it during a deployment?
2. What are the three ordered steps of a graceful shutdown sequence?
3. What should a good health-check endpoint verify?

Takeaway

A production process receives SIGTERM as a polite request to finish up — a graceful shutdown stops new connections, waits for in-flight work to complete, then exits cleanly, and a health check should verify genuine readiness, not just that the process exists.

Summary

This final lesson covered the graceful shutdown sequence as an explicit state machine and what a meaningful health-check endpoint should and shouldn't verify, completing the operational-readiness picture for a real backend service.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.