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.
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.
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.
ID: error-resilience.graceful-degradation-shutdown.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')
})