1. Overview

systemd is the modern system and service manager in Linux. It manages system initialization by starting the services using their unit files.

In a unit file, we can specify not only the executable that starts a service but also an executable that systemd runs while the system is being shut down. One reason for calling an executable during shutdown might be for cleanup and releasing resources.

In this tutorial, we’ll discuss how to run a script using systemd while shutting down the system.

2. Example Setup

In this section, we discuss the setup we’ll use.

2.1. Service Scripts

We’ll run the following shell script, simple_service.sh, as a service using systemd in our example:

$ cat simple_service.sh
#!/bin/bash

function handle_signal()
{
    exit 0
}

trap handle_signal SIGTERM

while true
do
    sleep 1
done

The script just calls the sleep 1 command periodically in an infinite while loop. Before entering the while loop, we set up a signal handler using trap handle_signal SIGTERM. When we stop the running script by sending the SIGTERM signal, the handle_signal() function is called. systemd also sends the SIGTERM signal to the service while stopping the service. Therefore, we ensure a proper shutdown of the service.

Additionally, we’ll use the following script, simple_service_stop.sh:

$ cat simple_service_stop.sh
#!/bin/bash

echo "Simple Service exiting..."

As we’ll see shortly, systemd executes this script when we stop the service or shut down the system. The script just prints “Simple Service exiting…” and exits.

Finally, let’s grant both scripts execute permissions using chmod:

$ chmod +x simple_service.sh simple_service_stop.sh

Now, we’re ready to set up the service unit file.

2.2. Unit File

We use the following unit file, simple_service.service, to run simple_service.sh as a service using systemd:

[Unit]
Description=Simple Service

[Service]
ExecStart=/home/alice/work/simple_service/simple_service.sh
ExecStop=/home/alice/work/simple_service/simple_service_stop.sh

[Install]
WantedBy=multi-user.target

The ExecStart option in the Service section of the unit file specifies the path of the script that systemd runs as a service. This script is simple_service.sh in the /home/alice/work/simple_service directory.

The ExecStop option in the Service section, on the other hand, specifies the path of the script that systemd runs while stopping the service. This script is simple_service_stop.sh in the /home/alice/work/simple_service directory.

2.3. Starting the Service

Let’s start our service using systemd:

$ cd /etc/systemd/system
$ sudo ln -s /home/alice/work/simple_service/simple_service.service
$ sudo systemctl enable simple_service
Created symlink /etc/systemd/system/multi-user.target.wants/simple_service.service → /home/alice/work/simple_service/simple_service.service.
$ sudo systemctl start simple_service

First, we create a symbolic link to the unit file in the /etc/systemd/system directory. Then, we enable the service using sudo systemctl enable simple_service. Finally, we start the service using the sudo systemctl start simple_service command. Since these commands require root privileges, we run them together with the sudo command.

Let’s now check whether our service is running using the systemctl status command:

$ systemctl status simple_service 
● simple_service.service - Simple Service
     Loaded: loaded (/etc/systemd/system/simple_service.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2024-06-28 07:33:48 EDT; 2min 57s ago
   Main PID: 3963 (simple_service.s)
      Tasks: 2 (limit: 4593)
     Memory: 540.0K
        CPU: 121ms
     CGroup: /system.slice/simple_service.service
             ├─3963 /bin/bash /home/alice/work/simple_service/simple_service.sh
             └─4131 sleep 5

Jun 28 07:33:48 ubuntu-2204 systemd[1]: Started Simple Service.

Our service is up and running.

3. Testing the Setup

We’ll test our setup in this section.

3.1. Rebooting the System

We’ll now reboot the system and then examine the system log file after the system restarts. But, let’s first note down the time before rebooting the system:

$ date
Fri Jun 28 07:38:43 AM EDT 2024

Now, let’s reboot the system using the sudo reboot command:

$ sudo reboot

After the system restarts, we search for the last match of the output of the simple_service_stop.sh script in the system log file:

$ sudo grep -a "Simple Service exiting..." /var/log/syslog | tail -1
Jun 28 07:39:03 ubuntu-2204 simple_service_stop.sh[3415]: Simple Service exiting...

The Linux distro on our system is Ubuntu, so we search for the output in the /var/log/syslog system log file. The name of the system log file and its location depend on the Linux distro. For example, we use the /var/log/messages system log file for Red Hat.

Since the system log files are binary, we need to use the -a option of grep, which lets us process a binary file as if it were a text file. We search for the “Simple Service exiting…” string in the log file. There might be multiple matches, so we filter the output using the tail -1 command. The -1 option of tail lists the last match.

In some Linux distros like Debian 12, we may need to use the journalctl command to check the output of simple_service_stop.sh:

$ sudo journalctl -u simple_service | grep "Simple Service exiting..." | tail -1

The -u option of journalctl shows the messages for the specified unit, which is simple_service in our example.

As the output on our Ubuntu system shows, systemd called the simple_service_stop.sh script at 07:39:03 after we ran the date command at 07:38:43 just before the reboot.

Let’s check the status of simple_service after the reboot:

$ systemctl status simple_service 
● simple_service.service - Simple Service
     Loaded: loaded (/etc/systemd/system/simple_service.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2024-06-28 07:39:13 EDT; 1h 9min ago
   Main PID: 636 (simple_service.s)
      Tasks: 2 (limit: 4593)
     Memory: 556.0K
        CPU: 2.782s
     CGroup: /system.slice/simple_service.service
             ├─636 /bin/bash /home/alice/work/simple_service/simple_service.sh
             └─6114 sleep 5

Jun 28 07:39:13 ubuntu-2204 systemd[1]: Started Simple Service.

The start time of the service after the reboot is 07:39:13 according to the output.

Therefore, systemd runs the script we specify in the ExecStop option of the unit file during system shutdown.

3.2. Stopping the Service Manually

Although we rebooted the system in the previous section to check whether systemd calls simple_service_stop.sh during the shutdown, we can also check the execution of the script by stopping the service. We can stop the service using the systemctl stop command:

$ sudo systemctl stop simple_service

We use systemctl stop together with sudo as it requires root privileges. Let’s now check the status of simple_service using systemctl status:

$ systemctl status simple_service
○ simple_service.service - Simple Service
     Loaded: loaded (/etc/systemd/system/simple_service.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Thu 2024-06-28 08:52:51 EDT; 3s ago
    Process: 636 ExecStart=/home/alice/work/simple_service/simple_service.sh (code=exited, status=0/SUCCESS)
    Process: 6283 ExecStop=/home/alice/work/simple_service/simple_service_stop.sh (code=exited, status=0/SUCCESS)
   Main PID: 636 (code=exited, status=0/SUCCESS)
        CPU: 2.923s

Jun 28 07:39:13 ubuntu-2204 systemd[1]: Started Simple Service.
Jun 28 08:52:51 ubuntu-2204 systemd[1]: Stopping Simple Service...
Jun 28 08:52:51 ubuntu-2204 simple_service_stop.sh[6283]: Simple Service exiting...
Jun 28 08:52:51 ubuntu-2204 simple_service.sh[636]: Terminated
Jun 28 08:52:51 ubuntu-2204 systemd[1]: simple_service.service: Deactivated successfully.
Jun 28 08:52:51 ubuntu-2204 systemd[1]: Stopped Simple Service.

As it’s apparent from the output, systemd called simple_service_stop.sh while stopping the service.

4. Conclusion

In this article, we discussed how to run a script using systemd while shutting down the system. First, we set up the test system. We saw that we can use the ExecStop option in the service unit file for specifying the executable that systemd runs while shutting down the system.

Then, we tested our setup first by shutting down the system and examining the system logs after reboot. Finally, we tested the setup by stopping the service using the systemctl stop command. We saw that systemd called the script specified in the ExecStop option in the unit file in both tests.