DEV Community

Young Gao
Young Gao

Posted on

Graceful Shutdown in Node.js: Stop Losing Requests During Deploys

Graceful Shutdown in Node.js: Stop Losing Requests During Deploys

Your deploy kills in-flight requests. Users get 502s. Database transactions hang open.

The Problem

SIGTERM arrives. process.exit() runs. Active connections drop. Users see errors.

The Fix

import http from "http";
import { Pool } from "pg";

const pool = new Pool();
const app = express();
const server = http.createServer(app);

let isShuttingDown = false;
app.use((req, res, next) => {
  if (isShuttingDown) return res.status(503).json({ error: "Shutting down" });
  next();
});

async function shutdown(signal: string) {
  console.log(`Received ${signal}. Starting graceful shutdown...`);
  isShuttingDown = true;
  server.close(() => console.log("HTTP server closed"));
  await pool.end();
  process.exit(0);
}

process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

setTimeout(() => { console.error("Forced shutdown"); process.exit(1); }, 30000);
Enter fullscreen mode Exit fullscreen mode

The Shutdown Order

  1. Stop accepting new connections (server.close)
  2. Reject new requests with 503 (isShuttingDown middleware)
  3. Wait for in-flight requests to complete
  4. Close database connections (pool.end)
  5. Close Redis, message queues, other external connections
  6. Force kill after timeout (safety net)

Health Check Integration

Your load balancer needs to know you are shutting down:

app.get("/healthz", (req, res) => {
  if (isShuttingDown) return res.status(503).send("shutting down");
  res.status(200).send("ok");
});
Enter fullscreen mode Exit fullscreen mode

Docker + Kubernetes

Kubernetes sends SIGTERM and waits terminationGracePeriodSeconds (default 30s) before SIGKILL. Your app must finish within that window.

In Docker, use exec form CMD so SIGTERM reaches Node directly:

CMD ["node", "dist/server.js"]
Enter fullscreen mode Exit fullscreen mode

Not CMD node dist/server.js which runs via shell and swallows signals.

Common Mistakes

  1. Calling process.exit() immediately - Kills in-flight requests
  2. No timeout - A stuck request hangs shutdown forever
  3. Shell form in Dockerfile - Signals never reach Node
  4. Forgetting database cleanup - Connections leak and exhaust pool

Part of my Production Backend Patterns series. Follow for more practical backend engineering.

Top comments (0)