๐Ÿ›‘Graceful ShutdownLESSON

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:

  1. Stop accepting new connections
  2. Finish serving requests already in progress
  3. 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

MethodBehaviour
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?