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
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
- Jobs can be made to depend on other systemd 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
- time relative to a real time (using
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
Every scheduled task requires its own service file. Our example is a backup script which we will set up to run weekly.
Do not use line-end comments behind time values, as those result in an parsing error. E.g.,
OnBootSec=15min #my comment will not work!
[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
Run On Intervals
If you wish to run backups according to a calendar event, use the
[Unit] Description=weekly full backup [Timer] # see systemd.time(7) manual page for other scheduling options OnCalendar=weekly # allow flexibility in start time AccuracySec=1h # run immediately if we missed a backup for some reason Persistent=true [Install] WantedBy=timers.target
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
[Unit] Description=weekly and post-boot backup [Timer] # run 15 minutes after boot time OnBootSec=15min # run 1 week after service was last started OnUnitActiveSec=1w AccuracySec=10min [Install] WantedBy=timers.target
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: 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: Starting weekly and post-boot backup. Jul 07 19:21:53 host systemd: Started weekly and post-boot backup.
- 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
This describes a few caveats involved when migrating from cron jobs to systemd timer units.
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.
- https://fedoraproject.org/wiki/Features/SystemdCalendarTimers - systemd calendar timers on the Fedora Project wiki