Graceful shutdown in Go

A quick guide to releasing resources properly

Emre Tanriverdi
4 min readApr 29, 2021

Go is widely being used for backend programming and its community is growing larger each day. Personally I love coding in Go.
I wanted to show a quick guide on how to gracefully shutdown our Go applications, which is actually a very easy process.

This story is about how to implement graceful shutdown in Go with a
step-by-step guide.
So this story considers you already know why we need to gracefully shutdown our applications from other programming languages.

Signals

A signal is an event generated from UNIX and Linux systems in response to some condition and Go can listen to these events in app-level.

There are lots of Signals, but our concern is only shutdown signals. So let’s look at them a little bit:

SIGTERM gets sent as the generic software termination signal that is sent for almost all shutdown events (except for those below).

SIGKILL gets sent as a termination signal that is sent for quit immediately” events and generally should not be interfered with.

SIGINT gets sent when user inputs an interrupt signal (such as Ctrl+C).
- similar to SIGTERM, but for user events -

SIGQUIT gets sent when user inputs a quit signal (Ctrl+D).
- similar to SIGKILL, but for user events (such as force quit) -

Ideally, for production environment, listening to SIGTERM should be enough. (Kubernetes pods send SIGTERM signal when terminating.)
SIGINT is safe to listen and it’s a good practice for local debugging too.

So, SIGINT and SIGTERM covers almost all possible scenarios, as long as we catch and handle the signals, our app should be fine.

Using Channels for catching Signals

If you are familiar with Concurrency in Go, then you know about Channels.
If you are not, here is my guide about the topic: Concurrency in Go.

Art by @ashleymcnamara on Twitter

What we need here is to have a code block to listen for “shutting down” signal and do our custom (releasing resources) behavior based on this information.

Since Channels wait (because of their blocking behavior) up until receiving a message, it’s a perfect fit for this job.

So we can combine Channels with os.Signal as above:

When we run our app, “Done!” won’t get printed, it will wait up until gracefulShutdown channel receives a message.

Since gracefulShutdown is listening to SIGINT and SIGTERM, our app will wait up until the program is terminated.

So when we shut down the app, the output is:

It works!

But generally this is not enough, we need to also release the resources from the sources we use such as external apis, databases etc.

Code directly from Go’s documentation:

golang.org/pkg/context

Calling cancel() releases the resources associated with a specific context.
So we can combine our graceful shutdown channel with our context to achieve the behavior we wanted:

Wait for shutdown signal, release the resources properly.

So let’s do it:

Now you can customize this code as any way you want, let’s say this is a hourly job and you want to send a “terminated” message to a message queue, you can do this as follows:

gist on GitHub

Done!

All feedbacks are welcome and I hope it was helpful. :)

Thank you for reading! ❤️

--

--