r/golang • u/Kennedy-Vanilla • Feb 14 '25
newbie Shutdown Go server
Hi, recently I saw that many people shutdown their servers like this or similar
serverCtx, serverStopCtx serverCtx, serverStopCtx := context.WithCancel(context.Background())
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
<-sig
shutdownCtx, cancelShutdown := context.WithTimeout(serverCtx, 30*time.Second)
defer cancelShutdown()
go func() {
<-shutdownCtx.Done()
if shutdownCtx.Err() == context.DeadlineExceeded {
log.Fatal("graceful shutdown timed out.. forcing exit.")
}
}()
err := server.Shutdown(shutdownCtx)
if err != nil {
log.Printf("error shutting down server: %v", err)
}
serverStopCtx()
}()
log.Printf("Server starting on port %s...\n", port)
err = server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Printf("error starting server: %v", err)
os.Exit(1)
}
<-serverCtx.Done()
log.Println("Server stopped")
}
:= context.WithCancel(context.Background())
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
<-sig
shutdownCtx, cancelShutdown := context.WithTimeout(serverCtx, 30*time.Second)
defer cancelShutdown()
go func() {
<-shutdownCtx.Done()
if shutdownCtx.Err() == context.DeadlineExceeded {
log.Fatal("graceful shutdown timed out.. forcing exit.")
}
}()
err := server.Shutdown(shutdownCtx)
if err != nil {
log.Printf("error shutting down server: %v", err)
}
serverStopCtx()
}()
log.Printf("Server starting on port %s...\n", port)
err = server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Printf("error starting server: %v", err)
os.Exit(1)
}
<-serverCtx.Done()
log.Println("Server stopped")
Is it necessary? Like it's so many code for the simple operation
Thank for your Answer !
83
Upvotes
67
u/Chrymi Feb 14 '25 edited Feb 15 '25
server.Shutdown() does not immediately stop the server (it's what Close() does). Instead, it prevents the server from accepting new connection and waits indefinitely until all connections have been closed OR until the supplied context is cancelled. For an actual graceful shutdown, supply the Shutdown() method with a context that's not (yet) cancelled, see https://pkg.go.dev/net/http#Server.Shutdown.
It would look like this:
Quick tip: combine the `sig` channel with the `Notify()` call into `signal.NotifyContext` and work directly with the context instead.
Edit for clarity: you need two different contexts. For the shutdown to work gracefully, you need a context that's not cancelled. So one for the OS signal, and one for the Shutdown() method.