Graceful Shutdown
A production HTTP server must not be killed abruptly. In-flight requests could be half-written, database connections could be left open, and buffers could be unflushed. Graceful shutdown gives the server a chance to finish ongoing work before exiting.
Why it matters
When Kubernetes sends SIGTERM (or a user presses Ctrl-C โ SIGINT), your server has a few seconds to:
- Stop accepting new connections
- Finish serving requests already in progress
- Close database connections and flush log buffers
Skip any of these and you risk data corruption, client errors, or zombie connections.
Registering signal handlers
signal.Notify delivers matching OS signals to the channel. The buffer size of 1 prevents the signal from being dropped if the program is momentarily busy.
The canonical shutdown sequence
Shutdown vs Close
| Method | Behaviour |
|---|---|
srv.Shutdown(ctx) | Stops accepting new connections, waits for active requests to finish (up to the context deadline) |
srv.Close() | Immediately closes all connections โ use only as a last resort |
Always prefer Shutdown with a reasonable timeout. Close is the emergency brake.
Why ListenAndServe runs in a goroutine
ListenAndServe blocks until the server is shut down. If you call it in the main goroutine, you can never reach the signal-handling code. Running it in a separate goroutine lets main proceed to the <-quit blocking receive.
After Shutdown is called, ListenAndServe returns http.ErrServerClosed โ that error is expected, so it's excluded from the fatal check.
Shutdown context deadline
The context.WithTimeout passed to Shutdown controls the maximum wait time for in-flight requests. If a request takes longer than the deadline, Shutdown returns an error and the server closes forcibly. Choose a deadline shorter than your container's termination grace period (typically 30 s in Kubernetes).
Background worker shutdown
Goroutines doing background work (sending emails, processing queues) should respect context cancellation:
Knowledge Check
What is the difference between http.Server.Shutdown and http.Server.Close?
Why must srv.ListenAndServe() be called in a goroutine?
What does the context passed to srv.Shutdown(ctx) control?