A graceful shutdown is the controlled process of stopping an application without interrupting active work. Instead of killing the process immediately, the system lets the application finish its ongoing tasks, close connections, and exit safely.
SIGTERM vs SIGKILL
These two signals behave differently:
SIGTERM
- Requests the process to shut down cleanly
- Can be caught and handled
- Sent by Docker, Kubernetes, PM2, systemd
- Gives the application time to close resources
SIGKILL
- Immediate termination
- Cannot be caught or ignored
- Used when the grace period expires (Docker/K8s will do this)
A typical container lifecycle:
textSIGTERM β wait (10β30 seconds) β SIGKILL
If the application doesnβt handle SIGTERM properly, it will be killed during the grace period and may lose data.
How a Graceful Shutdown Should Work
- Stop accepting new requests
- Stop background workers / cron jobs
- Stop queue consumers
- Finish active requests
- Close database pools
- Close Redis/caching connections
- Flush logs
- Exit before SIGKILL arrives
Graceful Shutdown in Node.js (Full Production Example)
Includes:
- HTTP server
- PostgreSQL
- Redis
- Cron job
- Shutdown flow with safety timer
jsconst http = require("http");
const { Pool } = require("pg");
const Redis = require("ioredis");
const cron = require("node-cron");
const db = new Pool({ connectionString: process.env.DB_URL });
const redis = new Redis(process.env.REDIS_URL);
let isShuttingDown = false;
const cronJob = cron.schedule("* * * * *", async () => {
if (isShuttingDown) return;
console.log("cron running...");
});
const server = http.createServer(async (req, res) => {
if (isShuttingDown) {
res.writeHead(503); // Service unavailable
return res.end("Server shutting down");
}
const value = await redis.get("counter");
res.end(`Counter: ${value}`);
});
server.listen(3000, () => console.log("Server running on 3000"));
async function gracefulShutdown() {
if (isShuttingDown) return;
isShuttingDown = true;
console.log("Graceful shutdown started...");
cronJob.stop();
server.close(() => console.log("HTTP server closed"));
try {
await db.end();
console.log("DB closed");
} catch {}
try {
await redis.quit();
console.log("Redis closed");
} catch {}
setTimeout(() => {
console.log("Forced shutdown");
process.exit(1);
}, 5000);
}
process.on("SIGTERM", gracefulShutdown);
process.on("SIGINT", gracefulShutdown);
Graceful Shutdown in Go (Full Production Example)
Includes:
- HTTP
- PostgreSQL
- Redis
- Worker goroutine
- Timeout-based shutdown
gopackage main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/go-redis/redis/v8"
"github.com/jackc/pgx/v5/pgxpool"
)
var ctx = context.Background()
func main() {
db, _ := pgxpool.New(ctx, os.Getenv("DB_URL"))
redisClient := redis.NewClient(&redis.Options{
Addr: os.Getenv("REDIS_ADDR"),
})
stopWorker := make(chan struct{})
go func() {
ticker := time.NewTicker(1 * time.Minute)
for {
select {
case <-ticker.C:
log.Println("worker running...")
case <-stopWorker:
log.Println("worker stopped")
return
}
}
}()
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val, _ := redisClient.Get(ctx, "counter").Result()
w.Write([]byte(val))
}),
}
go func() {
log.Println("Server running on 8080")
server.ListenAndServe()
}()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, os.Interrupt)
<-sigs
log.Println("Graceful shutdown started...")
close(stopWorker)
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
server.Shutdown(shutdownCtx)
db.Close()
redisClient.Close()
log.Println("Shutdown complete")
}
Best Practices
-
Reject traffic with
503once shutdown begins - Avoid infinite shutdown waits; enforce a timeout
- Ensure cron jobs stop scheduling new tasks
- Close all external connections explicitly
- Confirm readiness/liveness probes in Kubernetes respect shutdown periods
Conclusion
A correct graceful shutdown protects application state, prevents data corruption, and ensures clean deploys. Handling SIGTERM properly and closing all critical resources is mandatory for production systems β especially in containerized environments.
If a version is needed specifically for Kubernetes, Docker Swarm, systemd services, or microservice architectures, it can be prepared as well.
Album of the blog:




