Graceful shutdown is the discipline of stopping a running program without dropping work in flight. When a process gets a termination signal, the naive response is to die immediately — but that can cut off open requests, corrupt partial writes, and leave queues in a half-processed state. A graceful shutdown instead stops accepting new work, finishes what it already started, releases resources, and then exits.
What changed in 2026
- Orchestrators made signals unavoidable. Kubernetes, serverless platforms, and autoscalers routinely start and stop instances. Handling SIGTERM correctly is now table stakes, not a nicety.
- Shorter grace periods are common. Platforms tightened default termination windows to speed up rollouts, so shutdown logic has to be fast and bounded, not open-ended.
- Structured lifecycle hooks spread. Frameworks across languages standardized on explicit startup and shutdown hooks, making it easier to register cleanup without hand-rolling signal handlers.
How graceful shutdown works
The pattern is almost always the same four steps:
- Catch the signal. The platform sends SIGTERM (and later SIGKILL if you ignore it).
- Stop accepting new work. Close the listener, stop pulling from the queue, and fail readiness checks so the load balancer stops routing to you.
- Drain in-flight work. Let open requests or jobs finish, up to a deadline.
- Release resources and exit. Close database pools, flush logs and buffers, then exit cleanly.
A minimal Go-style sketch:
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM)
<-sig
server.Shutdown(ctxWithTimeout) // stop new, drain existing
db.Close()
The key idea: shutdown must be bounded. If draining takes longer than the grace period, the platform sends SIGKILL and you lose the remaining work anyway.
SIGTERM vs SIGKILL
|
SIGTERM |
SIGKILL |
| Catchable |
Yes |
No |
| Intent |
Please stop cleanly |
Stop now |
| Who sends it first |
Orchestrator on shutdown |
Orchestrator after grace period |
| Your job |
Drain and clean up |
Nothing — you are gone |
The whole point of graceful shutdown is to finish your work during the SIGTERM window so SIGKILL never has to be used.
When it matters most
Graceful shutdown matters any time losing in-flight work has a cost: HTTP servers with open requests, workers pulling from a queue, streaming consumers holding offsets, and anything mid-write to a database. It matters less for stateless, idempotent jobs that can simply be retried — though even there, closing connections cleanly avoids noisy errors. Long-lived connections and background jobs are the trickiest; those often interact with concurrency issues covered in race conditions.
Common pitfalls
- Failing readiness too late. If you keep passing health checks while shutting down, the load balancer keeps sending traffic into a dying process. Fail readiness first.
- Unbounded drains. Waiting "until everything finishes" invites a SIGKILL mid-cleanup. Always set a deadline shorter than the grace period.
- Ignoring buffered writes. Logs, metrics, and batched writes sitting in memory are lost if you exit before flushing. Flush explicitly.
- Blocking the signal handler. Doing heavy synchronous work directly in the handler can deadlock. Signal, then coordinate shutdown in the main flow.
FAQ
What signal should I handle for shutdown?
SIGTERM is the standard "please stop" signal from orchestrators and init systems. Also handle SIGINT for local Ctrl-C. You cannot catch SIGKILL.
How long should a graceful shutdown take?
Shorter than the platform grace period (often around 30 seconds by default). Set your drain deadline a few seconds under that so cleanup still runs.
What is the difference between draining and shutting down?
Draining is the middle step: refusing new work while finishing existing work. Shutdown is the whole lifecycle, ending in process exit.
Do stateless services need graceful shutdown?
Less critically, but still yes — closing connections and flushing logs cleanly avoids error spikes during every deploy and scale-down.
Where to go next