As a golang developer, ensuring that our Go programs run reliably and uninterrupted is paramount. One way to achieve this is by implementing an auto restart mechanism. However, unlike Node.js, Go does not have a popular process manager like pm2 that provides this functionality out-of-the-box. However, this doesn’t mean that Go developers are left without options. Thankfully, we can leverage the powerful Linux built-in service manager called systemd to achieve similar functionality.In this tutorial, we will explore how to use systemd to create a reliable and robust auto restart mechanism that restarts our Go programs on failure. So, let’s dive in!
Agenda
- Systemd overview and how it be of use in our case.
- Create a test program.
- Create a custom systemd service/unit file.
- Load and start our service.
Systemd and it’s role?
Systemd is a powerful init system and service manager available in most Linux distributions. Although systemd can manage a variety of resources represented by units, in this tutorial our focus will be on systemd service units utilizing which we can define and manage services, including our Go programs, with features like auto restart upon failure. This will let us harness the capabilities of systemd to create a reliable auto restart mechanism for our Go programs.
Create a test golang server
An example scenario where an auto restart functionality could be necessary is when developing a server or REST API that needs to remain operational at all times. Therefore, for the sake of this tutorial we will keep things simple and create a basic http server that responds with “OK”. The process will be same for any other go program.
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "OK")
})
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println(err)
}
}
Next we need to build our server
go build -o go_server
Create and define a systemd service
Now all systemd resources including services are represented by units and are configured using unit files, which include settings such as the program’s path, user privileges, restart conditions etc. Therefore, for systemd to be able to mange our go executable, we will need to first create what is known as a unit file of type service which defines our service. So, let’s call our service my_custom_server. Please note that all service unit files have a .service extension. So here the unit file for our my_custom_server service would be my_custom_server.service
Now a simple question that may come to our mind is where do we create our custom my_custom_server.service file? Well, for services that apply to the entire system, the convention is to place our custom .service file into /etc/systemd/system. So let’s create our unit file.
cd /etc/systemd/system
sudo nano my_custom_server.service
[Unit]
Description=My custom golang server
[Service]
Restart=on-failure
WorkingDirectory=/root/myprogram
ExecStart=/root/myprogram/go_server
[Install]
WantedBy=multi-user.target
Basic service unit file overview
- [Unit]: – defines basic info about our service
- Description: This line provides a brief description of the unit which in this case is “My custom golang server”
- Description: This line provides a brief description of the unit which in this case is “My custom golang server”
- [Service]: – defines our service
- Restart=always
This line specifies the restart behavior of the service. If the service fails or killed, systemd will automatically restart it. We can also use on-failure which will only restart in case of non-zero exit code. - WorkingDirectory=/root/myprogram
This line sets the working directory for the service and ensures that the service executes in the specified directory. In this case, it’s set to/root/myprogram
. - ExecStart=/root/myprogram/go_server
This line defines the command to start the service. It specifies the executable file or command systemd will execute when starting the service. In this case, it points to the/root/myprogram/go_server
executable.
- Restart=always
- [Install]:
- WantedBy=multi-user.target
This line specifies the target that the unit should be associated with during installation. The “multi-user.target” is a standard target in systemd that represents the normal multi-user system state. By specifying “multi-user.target,” the unit will be enabled and started when the system reaches the multi-user state during boot.
- WantedBy=multi-user.target
Load and start our service
To make systemd aware of our custom service file we just created, we will need to reload systemd daemon using the below command.
sudo systemctl daemon-reload
So with our systemd service activated, we will have control over starting, stopping, and restarting our Go program using systemd commands as shown below.
Start our service
sudo systemctl start my_custom_server
Check service status
sudo systemctl status my_custom_server
List our service
sudo systemctl list-units --type=service
Verify our auto restart mechanism
So to verify the effectiveness of our auto restart mechanism, we will intentionally terminate our my_custom_server service and observe whether systemd automatically restarts it. This test will provide assurance that our Go program remains reliable and resilient.
sudo systemctl kill my_custom_server
After sometime hopefully systemd will restart our service and it will up again.
Restart our service
sudo systemctl restart my_custom_server
Stop our service
sudo systemctl stop my_custom_server
Ensure our service starts on reboot
sudo systemctl enable my_custom_server
Conclusion
In this tutorial, we demonstrated how to implement an auto restart mechanism for our Go programs using systemd services by leveraging systemd’s capabilities. With the auto restart functionality in place, our Go programs will experience enhanced reliability, guaranteeing uninterrupted operation.