Home Blog

How to gracefully shut down a Go server

by Michael Banzon <michael@banzon.dk> on 2024-12-27

When doing an HTTP server in Go the call to http.ListenAndServe(...) will block until the server stops. But it shouldn't stop. Ever.

That leaves us with a problem - we might want to stop the server!

There might be various reasons. Maybe we want to replace the binary with an updated version. Maybe we just want it to not run.

Fortunately instead of using the default .ListenAndServe(...) we can create our own http.Server like this:

server := &http.Server{
    Addr: ":8080",
    Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(5 * time.Second)
        w.Write([]byte("Slept 5 seconds! Hello, World!"))
    }),
}

This server, for the purpose of illustration, has only one simple handler that waits for five seconds and then returns the string.

After creating a server like this, we can shut it down gracefully:

err := server.Shutdown(context.Background())

Please notice that we pass a context.Context to the .Shutdown(...) function. That gives us the flexibility to control the shutdown process in greater details if needed (but for now we just use ).

Handling program shutdown

If we then setup a handler for signals (from the OS) to handle some events of interrest, for example syscall.SIGINT and syscall.SIGTERM we start a goroutine that will listen for any of these signals and that will then cal the .Shutdown(...) function on the server.

Like this:

// setup signal handler to gracefully shutdown the program
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

go func() {
    <-sig
    fmt.Println("Shutting down server...")
    err := server.Shutdown(context.Background())
    if err != nil {
        // handle the error
    }
}()

The signals from the OS will be sent to allow the program to gracefully shut down.

Putting it all together

I have put everything here together with a lot of helper functions and signals to keep all the parts working.

There is a channel done meant to signal that the program should exit. There is also a channel errCh that is used for sending errors.

The full code is in the repository on Github.

RSS Feed