javascript 170 lines · 1 tab

Container health checks and graceful shutdown patterns

Ryan Nakamura Feb 2026
1 tab
// Express app with health checks and graceful shutdown
const express = require('express');
const { createServer } = require('http');

const app = express();
const server = createServer(app);

// Application state
let isReady = false;
let isShuttingDown = false;
const connections = new Set();

// Track connections for graceful shutdown
server.on('connection', (conn) => {
  connections.add(conn);
  conn.on('close', () => connections.delete(conn));
});

// Health check endpoints
app.get('/health/live', (req, res) => {
  // Liveness: is the process alive and not deadlocked?
  if (isShuttingDown) {
    return res.status(503).json({
      status: 'shutting_down',
      timestamp: new Date().toISOString(),
    });
  }

  res.json({
    status: 'alive',
    uptime: process.uptime(),
    memoryUsage: process.memoryUsage(),
    timestamp: new Date().toISOString(),
  });
});

app.get('/health/ready', (req, res) => {
  // Readiness: can this instance handle requests?
  if (!isReady || isShuttingDown) {
    return res.status(503).json({
      status: 'not_ready',
      isReady,
      isShuttingDown,
    });
  }

  res.json({
    status: 'ready',
    timestamp: new Date().toISOString(),
  });
});

app.get('/health/startup', (req, res) => {
  // Startup: has the app finished initializing?
  if (!isReady) {
    return res.status(503).json({ status: 'starting' });
  }
  res.json({ status: 'started' });
});

// Middleware: reject requests during shutdown
app.use((req, res, next) => {
  if (isShuttingDown) {
    res.setHeader('Connection', 'close');
    return res.status(503).json({
      error: 'Server is shutting down',
    });
  }
  next();
});

// Application routes
app.get('/', (req, res) => {
  res.json({ message: 'Hello, World!' });
});

// Startup sequence
async function startup() {
  console.log('Starting application...');

  try {
    // Initialize database connection
    // await db.connect();
    console.log('Database connected');

    // Initialize cache
    // await redis.connect();
    console.log('Cache connected');

    // Warm up caches if needed
    // await warmupCache();

    isReady = true;
    console.log('Application is ready');
  } catch (error) {
    console.error('Startup failed:', error);
    process.exit(1);
  }
}

// Graceful shutdown
async function shutdown(signal) {
  console.log(`Received ${signal}, starting graceful shutdown...`);

  isShuttingDown = true;
  isReady = false;

  // 1. Stop accepting new connections
  server.close(() => {
    console.log('HTTP server closed');
  });

  // 2. Wait for in-flight requests (with timeout)
  const shutdownTimeout = setTimeout(() => {
    console.error('Shutdown timeout, forcing exit');
    process.exit(1);
  }, 30000); // 30 second timeout

  // 3. Close existing keep-alive connections
  for (const conn of connections) {
    conn.end();
  }

  // Give connections time to drain
  await new Promise((resolve) => setTimeout(resolve, 5000));

  // 4. Destroy remaining connections
  for (const conn of connections) {
    conn.destroy();
  }

  try {
    // 5. Close external connections
    // await db.disconnect();
    console.log('Database disconnected');

    // await redis.disconnect();
    console.log('Cache disconnected');

    clearTimeout(shutdownTimeout);
    console.log('Graceful shutdown complete');
    process.exit(0);
  } catch (error) {
    console.error('Error during shutdown:', error);
    clearTimeout(shutdownTimeout);
    process.exit(1);
  }
}

// Signal handlers
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

// Unhandled errors
process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
  shutdown('uncaughtException');
});

process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
});

// Start server
const PORT = process.env.PORT || 3000;

server.listen(PORT, async () => {
  console.log(`Server listening on port ${PORT}`);
  await startup();
});
1 file · javascript Explain with highlit

Health checks verify container readiness and liveness. The HEALTHCHECK Dockerfile instruction defines container-level checks. Kubernetes readinessProbe gates traffic routing—failing probes remove Pods from Service endpoints. livenessProbe detects deadlocked processes and triggers restarts. startupProbe handles slow-starting applications. HTTP probes check endpoints, TCP probes verify port connectivity, and exec probes run commands. Graceful shutdown handles SIGTERM to drain connections before stopping. The preStop lifecycle hook delays shutdown for load balancer updates. terminationGracePeriodSeconds sets the maximum shutdown time. Connection draining ensures in-flight requests complete. Proper health checks and shutdown prevent dropped requests during deployments.


Related snips

ruby
require "timeout"

module HealthCheck
  class Probe
    Result = Struct.new(:name, :status, :latency_ms, :critical, :error, keyword_init: true) do
      def healthy?

Health Check Endpoint with Dependency Probes

rails reliability health-check
by codesnips 3 tabs
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-reader
  namespace: production
---

Kubernetes RBAC roles with least privilege service accounts

kubernetes rbac least-privilege
by Kai Nakamura 1 tab
typescript
import { Pool, PoolClient, Client, QueryResult, QueryResultRow } from 'pg';

const MAX_LIFETIME_MS = 30 * 60 * 1000;

export const pool = new Pool({
  connectionString: process.env.DATABASE_URL,

Postgres connection pooling with pg + max lifetime

node postgres connection-pooling
by codesnips 3 tabs
yaml
# Headless Service for stable DNS
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: production

Kubernetes StatefulSets for stateful workloads

kubernetes k8s statefulsets
by Ryan Nakamura 1 tab
javascript
// k6 Load Test Configuration
// Run: k6 run load-test.js --env BASE_URL=https://api.example.com

import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';

Load testing APIs with k6 for performance validation

k6 load-testing performance
by Ryan Nakamura 1 tab
hcl
# Using the module

module "api_service" {
  source = "./modules/ecs_service"

  service_name       = "api"

Terraform modules for reusable infrastructure

terraform modules iac
by Ryan Nakamura 2 tabs

Share this code

Here's the card — post it anywhere.

Container health checks and graceful shutdown patterns — share card
Link copied