systemd/Timers

From ArchWiki
< Systemd
Revision as of 21:25, 6 August 2014 by Gabx (Talk | contribs) (Run On Intervals: add tip to avoid too many services at 00:00:00)

Jump to: navigation, search

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:

  • Status and logging outputs can be got through journalctl. This enables proper debugging.
  • Jobs are decoupled from their timers. This allows jobs to be easily run independently of their timers or for multiple timers to trigger a single job.
  • Each job can be configured to run in a specific environment (see systemd.exec(5)).
  • Jobs can be made to depend on other systemd units.

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 such as the last boot (using OnBootSec) or the time the service was last activated (using OnUnitActiveSec).
  • time relative to a real time (using OnCalendar).

For each timer file, a matching unit service file must exist, describing the unit to activate when the timer elapses. By default, this will be a service by the same name as the timer (except for the suffix). For example, foo.timer will activate and control foo.service. As the service will be started by the timer, the service does not need an [Install] section. Instead, you should specify WantedBy=timers.target in the timer's [Install] section and enable the timer unit. See systemd.timer(5) for a full list of options available for timer units.

To see all of your active timers with time details, run:

$ 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

Example

Every scheduled task requires its own service file. Our example is a backup script which we will set up to run weekly.

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

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

There are a couple ways you could make a timer for this service, but the timer must have the same name as the service (i.e. myBackup.timer) or specify Unit=myBackup.service.

Run On Intervals

If you wish to run backups according to a calendar event, use the OnCalendar option.

/etc/systemd/system/myBackup.timer
[Unit]
Description=weekly full backup

[Timer]
OnCalendar=weekly # see systemd.time(7) manual page for other scheduling options
AccuracySec=1h    # allow flexibility in start time
Persistent=true   # run immediately if we missed a backup for some reason

[Install]
WantedBy=timers.target
Tip: Using OnCalendar = weekly, as described above, will trigger the service once in a week at 00:00:00. In case you run many heavy services, best would be to trigger part of them in another time than 00:00:00. As an example, you can write such calendar event: OnCalendar = Wed *-*-* 23:15:00. In that way, your service will be triggered each Wednesday at 23:15:00

Run After Booting

Alternatively, if you want to run backups every time the system boots (and every week while between reboots), you can combine OnBootSec with OnUnitActiveSec.

/etc/systemd/system/myBackup.timer
[Unit]
Description=weekly and post-boot backup

[Timer]
OnBootSec=15min    # run 15 minutes after boot time
OnUnitActiveSec=1w # run 1 week after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target
Note: In case of resource-eating services, do not set the timer too close to boot. It could seriously delay your login and X sessions.

Management

You do not need to enable or start the service file yourself, simply enable and start the timer. To double check that your new timer and service are enabled, run the following commands:

$ systemctl status myBackup
● myBackup.service - full system 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)

Jul 09 10:27:45 host systemd[1]: Started full system backup.
$ systemctl status myBackup.timer
● myBackup.timer - weekly and post-boot backup
   Loaded: loaded (/etc/systemd/system/myBackup.timer; enabled)
   Active: active (waiting) since Mon 2014-07-07 19:21:53 CEST; 1 day 16h ago

Jul 07 19:21:53 host systemd[1]: Starting weekly and post-boot backup.
Jul 07 19:21:53 host systemd[1]: Started weekly and post-boot backup.
Note:
  • The service is marked as inactive since its status indicates whether the backup (not the timer) is currently running
  • The timer is marked as waiting as it is waiting for the next trigger to start the backup

Caveats

This describes a few caveats involved when migrating from cron jobs to systemd timer units.

Parallelization

Currently, there is no built-in way to spread timers out evenly across a given interval. While AccuracySec is sometimes offered as a solution, the randomization it implies is designed such that it is synchronised between all local timer units, and is not equivalent to cron’s random delay configuration. E.g., all daily timer units with AccuracySec=15m will trigger the associated jobs at the same point in time between 00:00 and 00:15.

See also