Login With Github

Systemd Timer Tutorial With An Automatic Mail Sending Example

Systemd is a very powerful system booter for Linux.

In this article, we will introduce how to use Systemd to set up timed tasks with a simple example. It is not only practical, but also can be used as a tutorial for Systemd.

1. Timed Tasks

A so-called timed task is a task which is scheduled to be performed at one or more points in the future, such as receiving mails every five minutes, analyzing logs every day at two in the morning, and so on.

Linux systems usually use cron to set timed tasks. Systemd also has this feature, and its advantages are outstanding.

  • It can automatically generate logs. If you use Systemd's log analysis tool, it can be more convenient to locate the errors.
  • It can set the amount of memory and CPU usage, such as using up to 50% of the CPU.
  • It can perform very complex tasks, because it allows us to split tasks and rely on other Systemd units.

Next I will demonstrate a Systemd timed task: sending an email every hour.

2. Mail Script

First write a script mail.sh to send mails.

#!/usr/bin/env bash
echo "This is the body" | /usr/bin/mail -s "Subject" someone@example.com

Execute the script.

$ bash mail.sh

After executing, you will receive an email with the title of "Subject".

If your Linux system does not support sending mails, it is recommended to install ssmtp or msmtp. By the way, the usage of the mail command can be referred to here.

3. Systemd Unit

The very first step to learn Systemd is to understand what the "unit" is.

In short, the "unit" is the smallest functional unit of Systemd, and it's used to describe a single process. We build a large task management system by calling many small units. This is the basic work way of ​​Systemd.

Because Systemd can be used to do many things, there are many different types of units: probably a total of 12 types. For example, the Service unit is used for the background service, the Timer unit is used for the timer, and the Slice unit is used for the allocation of resources.

There is a unit description file for each unit, and it's placed across three directories.

  • /lib/systemd/system: system default unit files
  • /etc/systemd/system: user-defined unit files
  • /usr/lib/systemd/system: unit files for user-installed software

We can use the following command to view all unit files.

# view all units
$ systemctl list-unit-files

# view all Service units
$ systemctl list-unit-files --type service

# view all Timer units
$ systemctl list-unit-files --type timer

4. Unit Management Command

The following are common unit management commands.

# startup the unit
$ systemctl start [UnitName]

# close the unit
$ systemctl stop [UnitName]

# restart the unit
$ systemctl restart [UnitName]

# kill the unit process
$ systemctl kill [UnitName]

# view the unit status
$ systemctl status [UnitName]

# systemctl enable the unit automatically
$ systemctl enable [UnitName]

# systemctl disable the unit automatically
$ systemctl disable [UnitName]

5. Service Unit

As mentioned before, the Service unit is the task to be performed, such as  sending an email.

Creating a new Service is very simple, which is to create a new file in the /usr/lib/systemd/system directory, such as the mytimer.service file. For example, you can type the following code.

[Unit]
Description=MyTimer

[Service]
ExecStart=/bin/bash /path/to/mail.sh

As you can see, the Service unit file is divided into two parts.

The [Unit] part describes the basic information of the unit (ie, metadata), and the field of Description gives a brief introduction of the unit (ie.  MyTimer).

The [Service] part is used to customize actions, and Systemd provides many fields.

  • ExecStart: commands to be executed by systemctl start
  • ExecStop: commands to be executed by systemctl stop
  • ExecReload: commands to be executed by systemctl reload
  • ExecStartPre: commands to be executed before ExecStart automatically
  • ExecStartPost: commands to be executed after ExecStart automatically
  • ExecStopPost: commands to be executed after ExecStop automatically

Note that when defining, all paths must be written as absolute paths. For example, bash should be written as /bin/bash, otherwise Systemd won't find it.

Now start the Service.

$ sudo systemctl start mytimer.service

If everything is ok, you will receive an email.

6. Timer Unit

The Service unit just defines how to perform the task. And you must define the Timer unit in order to execute the Service periodically.

Create a new mytimer.timer file in the /usr/lib/systemd/system directory,  and type the following code.

[Unit]
Description=Runs mytimer every hour

[Timer]
OnUnitActiveSec=1h
Unit=mytimer.service

[Install]
WantedBy=multi-user.target

The Timer unit file is divided into several parts.

The [Unit] part defines the metadata.

The [Timer] part customizes the Timer. And Systemd provides the following fields.

  • OnActiveSec: How long does it take to start the task after the timer takes effect?
  • OnBootSec: How long does it take to start the task after the system starts?
  • OnStartupSec: How long does it take to start the task after the Systemd process starts?
  • OnUnitActiveSec: How long does it take for the unit to execute again after the unit has been executed the last time?
  • OnUnitInactiveSec: How long does it take to execute again since the timer has been turned off the last time?
  • OnCalendar: Execute based on absolute time, not relative time.
  • AccuracySec: If the task must be postponed for any reason, the maximum number of seconds to defer is 60 seconds by default.
  • Unit: The task to be executed. The default is the unit with the .service suffix of the same name.
  • Persistent: If the field is set, the corresponding unit will be automatically executed even if the timer does not start.
  • WakeSystem: Whether to wake the system automatically if the system goes to sleep.

In the above script, OnUnitActiveSec=1h indicates that the task will be performed per hour. Other ways of writing are: OnUnitActiveSec=*-*-* 02:00:00 means to be executed at two o'clock in the morning, and OnUnitActiveSec=Mon *-*-* 02:00:00 means to be executed at two o'clock in the morning every Monday. For details, please refer to the official documentation.

7. [Install] and target

Inside the mytimer.timer file, there is an [Install] part that defines the commands to be executed when setting the unit as systemctl enable or systemctl disable.

In the above script, the [Install] part only defines one field, which is WantedBy=multi-user.target. What it means is that if systemctl enable mytimer.timer is executed (as long as the Systemd is started, the timer automatically takes effect), then the timer belongs to multi-user.target.

The so-called Target refers to a group of related processes, a bit like the startup level below the init process mode. When any Target is started, all processes belonging to this Target will be started too.

Multi-user.target is the most commonly used Target. When a system is started in multi-user mode, mytimer.timer will be started together. The work way under the hood is quite simple. When executing the command of systemctl enable mytimer.timer, a symbolic link will be created inside the multi-user.target.wants directory, pointing to mytimer.timer.

8. Timer Related Commands

Next, start the newly created timer.

$ sudo systemctl start mytimer.timer

You will receive the email immediately and then receive the same email every hour.

Check the status of the timer.

$ systemctl status mytimer.timer

View all timers which are running.

$ systemctl list-timers

Turn off the timer.

$ sudo systemctl stop myscript.timer

Systemctl enable the timer automatically.

$ sudo systemctl enable myscript.timer

Systemctl disable the timer.

$ sudo systemctl disable myscript.timer

9. Log Related Commands

If an exception occurs, you need to check the log. Here are the commands that Systemd provides.

# view the entire log
$ sudo journalctl

# view the log for mytimer.timer
$ sudo journalctl -u mytimer.timer

# view the logs for mytimer.timer and mytimer.service
$ sudo journalctl -u mytimer

# view the latest log from the end
$ sudo journalctl -f

# view the log for mytimer.timer from the end
$ journalctl -f -u timer.timer

10. Reference Links

3 Comments

temp

there's mistake in section 3 (Systemd unit).

/etc/systemd/system is for user-defined unit files

/usr/lib/systemd/system is for unit files from user-installed software

Before systemd : add a line to a cron tab; done.

After systemd: this article

No thanks

Great tutorial! 
thanks :)