Go cookbook · Beginner
Shut down an HTTP server gracefully
Deploys kill the process mid-request, dropping connections and losing work.
Approach
Listen for SIGINT/SIGTERM with signal.NotifyContext, stop accepting new connections with
Server.Shutdown(ctx) and give in-flight requests a deadline. Close other resources afterwards.
Solution
package main
import (
"context"
"errors"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
mux := http.NewServeMux()
mux.HandleFunc("GET /slow", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
w.Write([]byte("done\n"))
})
srv := &http.Server{Addr: ":8080", Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go func() {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("server failed", "err", err)
os.Exit(1)
}
}()
slog.Info("listening", "addr", srv.Addr)
<-ctx.Done() // wait for a signal
slog.Info("shutting down")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
slog.Error("forced shutdown", "err", err)
}
// close database pools, flush telemetry, stop workers here
slog.Info("stopped")
}
Pitfalls
- A shutdown timeout longer than the orchestrator's grace period (Kubernetes default 30 s).
- Treating
http.ErrServerClosedfromListenAndServeas a failure. - Long-lived connections (WebSockets, streaming) aren't tracked by
Shutdown; signal them viaRegisterOnShutdown.
Variations
- Fail the readiness probe first, wait a few seconds for load balancers to notice, then shut down.
- Pass
ctxto background workers so they stop on the same signal.
Further reading: Server.Shutdown
Reference: https://pkg.go.dev/net/http#Server.Shutdown
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof