Graceful shutdown

When your orchestrator deploys a new version, it sends your process a SIGTERM and then, after a grace period, a SIGKILL. Everything in between is your chance to finish in-flight work and shut down cleanly. A service that ignores SIGTERM will get killed mid-request, corrupt data, and make the deploy look flaky. A service that handles it will be boring, which is the goal.

after/shutdown.ts
typescript
import { Server } from 'http';
import { logger } from './logger';
import { client as dbClient } from './db';

export function installShutdown(server: Server) {
  const shutdown = async (signal: string) => {
    logger.info({ signal }, 'shutdown requested');

    // Stop accepting new connections
    server.close((err) => {
      if (err) {
        logger.error({ err }, 'error closing HTTP server');
        process.exit(1);
      }
    });

    // Give in-flight requests a bounded window to finish
    setTimeout(() => {
      logger.warn('shutdown timed out, forcing exit');
      process.exit(1);
    }, 10_000).unref();

    // Close database pool
    try {
      await dbClient.end();
      logger.info('database connection closed');
    } catch (err) {
      logger.error({ err }, 'error closing database');
    }

    process.exit(0);
  };

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

Stop accepting new connections, finish in-flight requests, close the database, exit.

Read the shutdown function top to bottom. Log that a shutdown was requested so it is visible in your logs. Stop accepting new connections. Give existing requests a bounded window to finish, usually ten seconds. Close connections to Postgres and Redis. Exit with 0 if everything cleaned up, 1 if something went wrong. SIGTERM and SIGINT both route through the same handler.

One subtle detail: setTimeout(...).unref(). Without unref, the pending timer keeps the event loop alive, which defeats the whole point of shutting down cleanly. unref tells Node to ignore that timer when deciding whether the process can exit. Small detail, saves a whole class of hanging-process bugs.

Validation checklist: Make your service observable

Loading practice…

Checkpoint: Observability checkpoint

Loading practice…