Writing systemd enabled applications in Go

Writing a daemon in Go is really easy given the extensive language environment it provides out of the box. Making your daemon work nicely with Systemd is a little more challenging, especially if want to make use of the Systemd services like socket activations to spawn our daemon on demand and using the journal for logging purposes.

Suppose we’ve written a simple HTTP server that we want to run as daemon. Our server simple logs each incoming request to the journal and does not return anything (you figure out why we want to do this). By letting Systemd manage the socket on which our server is listening for us, we have to worry less about the possible security implications when we want to run our daemon on a non-ephemeral port. In order to do this, we need to play nice and along with the API Systemd provides us. Fortunately, there is a nice library called go-systemd by the people from CoreOS that allows us to talk directly to the Systemd APIs from Go.

For example, to use socket activation in our daemon, we can use something like:

import "net"
import "github.com/coreos/go-systemd/activation"

func Listener() net.Listener {
  listeners, err := activation.Listeners(true)
  if err != nil {
    panic(err)
  }
  if len(listeners) != 1 {
    panic("Unexpected number of socket activation fds")
  }
  return listeners[0]
}

If Systemd starts our service on our behalf, it passes the file descriptor of the listening socket to our application by means of some environment variables which we can use to handle incoming requests.

Logging information to the journal of Systemd from Go is as simple as calling a single function. The go-systemd library takes care of all the heavy-lifting for us:

import "github.com/coreos/go-systemd/journal"

func main() {
  if journal.Enabled() {
    journal.Print(journal.PriInfo, "all systems ready...")
  }
}

Before printing our message to the journal we first check whether it is actually present and enabled to play nice with systems that have disabled journal access. Besides that, it is a matter of calling the Print function with our message and the desired priority/log-level.

I’ve written a small sample project that uses both socket activation and the journal access from Go. You can find it on my Github account: https://github.com/jawi/mad.