Backend process handles graceful shutdown
Why it matters
A backend process that ignores SIGTERM drops in-flight requests immediately when the orchestrator (Kubernetes, Railway, Fly.io, Heroku) restarts or redeploys it. Users mid-checkout, mid-upload, or mid-auth flow receive hard errors with no recovery path. CWE-460 (improper cleanup on thrown exception) encompasses improper shutdown handling. Platforms send SIGTERM and wait a grace period (typically 30 seconds) before SIGKILL; failing to handle it means every deploy causes unannounced request failures that are invisible in your error tracking.
Severity rationale
Low in environments with short-lived requests, but causes guaranteed data loss and user-facing errors on every deployment in applications with long-running operations.
Remediation
Register SIGTERM and SIGINT handlers in your server entry point. Stop accepting new connections immediately, let in-flight requests finish, then close the database pool and exit.
// src/server.ts
const server = app.listen(3000)
async function shutdown(signal: string) {
console.log(`${signal} received — shutting down`)
server.close(async () => {
await pool.end()
process.exit(0)
})
// Force exit if drain takes too long
setTimeout(() => process.exit(1), 30_000)
}
process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))
Test graceful shutdown locally: run your server, send kill -SIGTERM <pid>, and verify in-flight requests complete before the process exits.
Detection
-
ID:
graceful-shutdown -
Severity:
low -
What to look for: Count all signal handlers (SIGINT, SIGTERM) and cleanup routines. Enumerate whether the server completes in-flight requests and closes database connections before shutting down. Look for signal handlers (SIGTERM, SIGINT) in the backend entry point. Check whether the application waits for in-flight requests to complete and closes the connection pool before exiting.
-
Pass criteria: Backend listens for SIGTERM and SIGINT; in-flight requests are completed before shutdown; connection pool is drained. At least 1 signal handler must be registered for SIGTERM with cleanup of at least 2 resources (DB connections, HTTP server).
-
Fail criteria: No signal handlers configured, or shutdown immediately kills connections without draining.
-
Skip (N/A) when: The application has no backend or is running in a serverless environment (AWS Lambda, Vercel Functions, etc.) that manages shutdown.
-
Cross-reference: For database connection pool cleanup, see
database-connection-pool. -
Detail on fail:
"No graceful shutdown handler. Server stops abruptly, killing in-flight requests"or"Signal handler exists but doesn't wait for requests to complete" -
Remediation: Add graceful shutdown:
// src/server.ts — graceful shutdown process.on('SIGTERM', async () => { await pool.end(); server.close(() => process.exit(0)) })// server.ts import express from 'express' import http from 'http' const app = express() const server = http.createServer(app) let isShuttingDown = false process.on('SIGTERM', () => { console.log('SIGTERM received, shutting down gracefully') isShuttingDown = true server.close(() => { console.log('Server closed') process.exit(0) }) // Force exit after 30 seconds setTimeout(() => { console.error('Forced shutdown') process.exit(1) }, 30000) }) server.listen(3000, () => { console.log('Server running') })
External references
- cwe · CWE-460 — Improper Cleanup on Thrown Exception
- iso-25010:2011 · reliability.recoverability
Taxons
History
- 2026-04-18·v1.0.0·Initial import from error-resilience·automated