go 60 lines · 8 steps

Graceful HTTP shutdown in Go

An HTTP server that listens in a goroutine and drains in-flight connections when it receives a termination signal.

Explained by highlit
1package main
2 
3import (
4 "context"
5 "errors"
6 "log"
7 "net/http"
8 "os"
9 "os/signal"
10 "syscall"
11 "time"
12)
13 
14func run() error {
15 mux := http.NewServeMux()
16 mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
17 w.WriteHeader(http.StatusOK)
18 })
19 
20 srv := &http.Server{
21 Addr: ":8080",
22 Handler: mux,
23 ReadTimeout: 5 * time.Second,
24 WriteTimeout: 10 * time.Second,
25 IdleTimeout: 120 * time.Second,
26 }
27 
28 serverErr := make(chan error, 1)
29 go func() {
30 log.Printf("listening on %s", srv.Addr)
31 if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
32 serverErr <- err
33 }
34 }()
35 
36 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
37 defer stop()
38 
39 select {
40 case err := <-serverErr:
41 return err
42 case <-ctx.Done():
43 stop()
44 log.Println("shutdown signal received, draining connections")
45 }
46 
47 shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
48 defer cancel()
49 
50 if err := srv.Shutdown(shutdownCtx); err != nil {
51 return srv.Close()
52 }
53 return nil
54}
55 
56func main() {
57 if err := run(); err != nil {
58 log.Fatalf("server error: %v", err)
59 }
60}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Running ListenAndServe in a goroutine lets the main flow stay free to wait on either an error or a shutdown signal.
  2. 2signal.NotifyContext turns OS signals into a cancellable context you can select on like any other event.
  3. 3Server.Shutdown with a timeout context drains active requests but needs a hard Close fallback when draining stalls.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Graceful HTTP shutdown in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code