Difference between revisions of "Systemd/Timers"

From ArchWiki
Jump to: navigation, search
m (Timer units: more precision about what unit file to enable)
m (Timer relative to a starting point: add a tip about the time after boot)
Line 95: Line 95:
 
* this service is marked as ''inactive'' as we don't need to keep it active once the backup is done, thus the lack of {{ic|RemainAfetrExit=yes}} in the unit file options.
 
* this service is marked as ''inactive'' as we don't need to keep it active once the backup is done, thus the lack of {{ic|RemainAfetrExit=yes}} in the unit file options.
 
* the timer is marked as ''waiting'' as it is waiting for the next trigger.  
 
* the timer is marked as ''waiting'' as it is waiting for the next trigger.  
 +
* in case of resource eating service, best is to set the timer far enough from boot. Otherwise, your login and start X processes will slow down seriously.
 
}}
 
}}
  

Revision as of 18:41, 13 July 2014

Systemd is capable of taking on a significant subset of the functionality of Cron through built in support for calendar time events (from systemd version 197) as well as monotonic time events. As of systemd version 212, it also has the foundations for being an anacron replacement through the use of the Persistent and OnCalendar options in timers.

While Cron has been a stalwart on the Linux landscape for years, it still provides no way to detect job failures, establish job dependencies, or allocate processes to cgroups. If you require any of this functionality, systemd provides a good structure to set up job scheduling. While doing so is slightly more cumbersome than relying on dcron or cronie, the benefits are not insignificant:

  • Last status and logging outputs can be got through journalctl. This enables proper debugging.
  • Systemd has a lot of options which are useful for setting the environment for the job to be done properly (eg IOSchedulingPriority, Nice or JobTimeoutSec).
  • These jobs can be made to depend on other systemd units if required.

Timer units

These files are systemd unit files with name ending in .timer. Writing these files follows the common options of all unit configuration files. Please refer to systemd#Writing custom .service files for a quick look at the overall syntax.

The timer specific configuration options are configured in a [Timer] section. There are two ways to define when the timed service will be triggered:

  • time relative to a starting point. Most commons starting points are the boot of your machine, using the OnBootSec= options, and the time your timed service was last activated, using the OnUnitActiveSec=.
  • time relative to real time, using the OnCalendar= option.

Choose the right option for your need, depending if you want the service be started after each boot or not.

For each timer file, a matching unit service file must exist, describing the unit to activate when the timer elapses. By default, a service by the same name as the timer (except for the suffix) will be activated. A very basic example would be to create a foo.timer to activate and control a foo.service.

As the service will be started by the timer unit, there is no need to write a [Install] section for the corresponding service. You will not enable the service, but the timer that will enable the service.Timer units need the [Install] section and they should be pulled in by timers.target.

See systemd.timer(5) for a full list of options available for the timer units.

To see all your active timers with time details, run the following command:

$ systemctl list-timers
NEXT                          LEFT        LAST                          PASSED     UNIT                         ACTIVATES
Thu 2014-07-10 19:37:03 CEST  11h left    Wed 2014-07-09 19:37:03 CEST  12h ago    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Fri 2014-07-11 00:00:00 CEST  15h left    Thu 2014-07-10 00:00:13 CEST  8h ago     logrotate.timer              logrotate.service

Basic example

Timer relative to a starting point

Let's say you want to start a backup script myBackup.service once in a week, and you want this service be started after each reboot. First, write the unit service file:

/etc/systemd/system/myBackup.service
[Unit]
Description=run a backup 

[Service]
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
ExecStart=/path/to/service/myBackup  

Then, write the matching timer unit file:

/etc/systemd/system/myBackup.timer
[Unit]
Description=run backup once in a week and after reboot

[Timer]
OnBootSec=15min      # service will start 15 minutes after you boot
OnUnitActiveSec=1w   # service will start every week after the timer was last activated

[Install]
WantedBy=timers.target

Now make a directory called timers.target.wants:

# mkdir /etc/systemd/system/timers.target.wants

Then, enable and start the timer. This will create a symlink to /etc/systemd/system/timers.target.wants/myBackup.timer. To ensure your new timer and service are enabled, run the following commands:

$ systemctl status myBackup
● myBackup.service - run a backup 
   Loaded: loaded (/etc/systemd/system/myBackup.service; static)
   Active: inactive (dead) since Wed 2014-07-09 10:27:45 CEST; 1h 8min ago
 Main PID: 22000 (code=exited, status=0/SUCCESS)
$ systemctl status myBackup.timer
● myBackup.timer - run backup once in a week and after reboot
   Loaded: loaded (/etc/systemd/system/myBackup.timer; enabled)
   Active: active (waiting) since Mon 2014-07-07 19:21:53 CEST; 1 day 16h ago
Note:
  • this service is marked as inactive as we don't need to keep it active once the backup is done, thus the lack of RemainAfetrExit=yes in the unit file options.
  • the timer is marked as waiting as it is waiting for the next trigger.
  • in case of resource eating service, best is to set the timer far enough from boot. Otherwise, your login and start X processes will slow down seriously.

Hourly, daily, weekly, monthly and yearly events

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

Reason: The method described below seems to be counterproductive in the words of Arch Linux developer Falconindy (a.k.a Dave Reisner); see Talk:Systemd/cron functionality#Counterproductive Suggestions for explanations and proposed corrections. (Discuss in Talk:Systemd/Timers#)

One strategy which can be used for recreating this functionality is through timers which call in targets. All services which need to be run hourly can be called in as dependencies of these targets. The strategy mentioned here has been detailed first in this blogpost.

Note: There is an AUR package systemd-cronAUR which uses a slightly different strategy to do what is being done here. If you wish to use this package, then the following scripts might need to be modified and their locations changed for them to work properly.
Note: There is also an AUR package systemd-crontab-generatorAUR which is intended to be a drop-in replacement for cron and anacron daemons. It works by parsing crontab files in known directories (like /etc/crontab and /var/spool/cron/) and generating timers and services in systemd. Though it is a beta product (while it works on several hosts owned by program's author), so it is not quite ready for production. Please read comments carefully before installation.

First, the creation of a few directories is required:

# mkdir /etc/systemd/system/timer-{hourly,daily,weekly,monthly,yearly}.target.wants

The following files will need to be created in the paths specified in order for this to work.

Hourly events

/etc/systemd/system/timer-hourly.timer
[Unit]
Description=Hourly Timer

[Timer]
OnBootSec=5min
OnCalendar=hourly
Unit=timer-hourly.target

[Install]
WantedBy=basic.target
/etc/systemd/system/timer-hourly.target
[Unit]
Description=Hourly Timer Target
StopWhenUnneeded=yes

Daily events

/etc/systemd/system/timer-daily.timer
[Unit]
Description=Daily Timer

[Timer]
OnCalendar=daily
Persistent=true
Unit=timer-daily.target

[Install]
WantedBy=basic.target
/etc/systemd/system/timer-daily.target
[Unit]
Description=Daily Timer Target
StopWhenUnneeded=yes

As of systemd 212, the functionality of anacron is implemented in any timer which utilizes the OnCalendar option through the use of the Persistent option. If you would like to understand this functionality better, please see the systemd.timer(5) man page.

Weekly events

/etc/systemd/system/timer-weekly.timer
[Unit]
Description=Weekly Timer

[Timer]
OnCalendar=weekly
Persistent=true
Unit=timer-weekly.target

[Install]
WantedBy=basic.target
/etc/systemd/system/timer-weekly.target
[Unit]
Description=Weekly Timer Target
StopWhenUnneeded=yes

Monthly events

/etc/systemd/system/timer-monthly.timer
[Unit]
Description=Monthly Timer

[Timer]
OnCalendar=monthly
Persistent=true
Unit=timer-monthly.target

[Install]
WantedBy=basic.target
/etc/systemd/system/timer-monthly.target
[Unit]
Description=Monthly Timer Target
StopWhenUnneeded=yes

Yearly events

/etc/systemd/system/timer-yearly.timer
[Unit]
Description=Yearly Timer

[Timer]
OnCalendar=yearly
Persistent=true
Unit=timer-yearly.target

[Install]
WantedBy=basic.target
/etc/systemd/system/timer-yearly.target
[Unit]
Description=Yearly Timer Target
StopWhenUnneeded=yes

Adding events

Adding events to these targets is as easy as dropping them into the correct wants folder. So if you wish for a particular job to take place daily, create a systemd service file.

For example, if you wish to run foo.service daily (which runs program bar), you would create the following file:

/etc/systemd/system/foo.service
[Unit]
Description=Starts program bar
Wants=timer-daily.timer

[Service]
User=                                          # Add a user if you wish the service to be executes as a particular user, else delete this line
Type=                                          # Simple by default, change it if you know what you are doing, else delete this line
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
ExecStart=/usr/bin/bar --option1 --option2     # More than one ExecStart can be used if required

[Install]
WantedBy=timer-daily.target

Then, start and enable the service.

Enable and start the timers

# systemctl enable timer-{hourly,daily,weekly,monthly,yearly}.timer 
# systemctl start timer-{hourly,daily,weekly,monthly,yearly}.timer

Starting events according to the calendar

If you wish to start a service according to a calendar event and not a monotonic interval (i.e. you wish to replace the functionality of crontab), you will need to create a new timer and link your service file to that. An example would be:

/etc/systemd/system/foo.timer
[Unit]
Description=foo timer

[Timer]
# To add a time of your choosing here, please refer to systemd.time manual page for the correct format
OnCalendar=Mon-Thu *-9-28 *:30:00
Persistent=true
Unit=foo.service

[Install]
WantedBy=basic.target

The service file may be created the same way as the events for monotonic clocks. However, take care to put them in the /etc/systemd/system/ folder.

Custom/example service files

Note: These example scripts assume that you have created the hourly, daily and weekly timers as described above.

Reflector

This service file may be used to update pacman's mirrorlist daily using the reflector script.

/etc/systemd/system/timer-daily.target.wants/reflector.service
[Unit]
Description=Update the mirrorlist

[Service]
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
Type=oneshot
ExecStart=/usr/bin/reflector --protocol http --latest 30 --number 20 --sort rate --save /etc/pacman.d/mirrorlist
Note: Check out the discussion for connectivity issues as reflector will fail and potentially stop your timer.
Note: Reflector's options should be tweaked according to users' criteria. Use case examples may be found in this forum post by Moderator/TU Xyne.

The modprobed_db service

This service is of great use to people who compile their own kernels because it reduces compilation time by a significant amount. Refer to the Modprobed_db page for further details.

/etc/systemd/system/modprobed_db.service
[Unit]
Description=Run modprobed_db
Wants=timer-daily.target

[Service]
Type=oneshot
RemainAfterExit=yes
User=enter user here
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
ExecStart=/usr/bin/modprobed_db store

[Install]
WantedBy=timer-daily.target

Then, start and enable the service

Hosts-update service

Package hosts-updateAUR uses mvps blocklist to update /etc/hosts.

/etc/systemd/system/timer-daily.target.wants/hosts-update.service
[Unit]
Description=Update hosts file
After=network.target

[Service]
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
ExecStart=/usr/bin/hosts-update

[Install]
WantedBy=timer-daily.target

See also