Schedule Jobs on Linux
Table of Contents
If you want to schedule a program to run on a Unix based OS, there are a handful of options, but the prominent ones found on Linux are cron and systemd timers.
Initially released in 1975,
cron has stood the test of time when it comes
to running a task on a schedule and it continues to be the standard solution
for all kinds of users.
How does it work #
There is a file called
crontab which tells the cron daemon what to run
and when to run it.
There are several cron daemons out there and the default one will depend
on what distro your using, but they all understand the same crontab, which
is what ends up getting executed in the end.
The cron package
on Arch is called
cronie or just
on Debian based distros.
The system crontab file, is found at
/etc/crontab, but there is also a place
where users can create their own crontab file by using
The system crontab does require you to specify which user to run the command as whether that be root or someone else.
On my system, the user crontabs are stored in the
directory in a file with the same name as $USER.
An overview of the syntax is as follows:
There are five fields you must specify:
minute, hour, day, month, day of the week.
┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of the month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; │ │ │ │ │ 7 is also Sunday on some systems) │ │ │ │ │ │ │ │ │ │ * * * * * command to execute
So running something every hour would be done like this:
0 * * * * echo "hourly job"
To run something every minute you can do:
* * * * * echo "minute job"
And to run something every day you can do:
* 0 * * * echo "day job"
You can also specify ranges with a
-, steps with a
/, and use
, as a separator
An example using all:
*/30 9-17 * * 1,5
Every 30 minutes past every hour from 9:00-5:00 on Monday and Friday.
Other tools #
It can get a bit confusing if you have a complicated set of times you wish to run a certain program.
A site I would recommend is https://crontab.guru/, which puts this syntax in plain English and has some common examples to use. As always the archwiki is a great resource for this kind of thing.
One thing to note is
cron expects a system to be persistent and always on,
meaning if your machine is down for whatever reason, the job will not be run
when it is booted back up. To combat this,
anacron was created and that is
what many desktop distributions use along with
Check jobs and logs #
As a user, to see what’s in your crontab, you can run
One way to check if what cron tasks were run or not is to check the log.
On a box running systemd, you can use
journalctl -u <cron daemon>
to check out the logs, which should report any jobs that were run.
The syntax and functionality of
anacron is different from
cron, but it
mostly serves the same purpose which is to run a task periodically.
The default anacrontab looks very similar to a crontab with some minor differences.
# /etc/anacrontab: configuration file for anacron # See anacron(8) and anacrontab(5) for details. SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # the maximal random delay added to the base delay of the jobs RANDOM_DELAY=45 # the jobs will be started during the following hours only START_HOURS_RANGE=3-22 #period in days delay in minutes job-identifier command 1 5 cron.daily nice run-parts /etc/cron.daily 7 25 cron.weekly nice run-parts /etc/cron.weekly @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
anacron, there are shortcuts for common periods of time.
Like @monthly shown in the example, there is also @daily, @weekly, and so on.
In this file you can also see
run-parts being used to run all the scripts
/etc/cron.* directories. This means that instead of creating a special
entry in a crontab, a user can simply place a script in one of these directories
and it will be run as the name suggests.
Time Resolution #
A minute is the shortest measure of time in cron, as opposed to systemd timers, which can handle seconds, milliseconds, and nanoseconds.
Of course there are other daemons that can handle this kind of task. And there is always the opportunity to write one :)
A commonplace solution is systemd-timers because systemd is more or less the default init system for most Linux distros.
Systemd timers #
Checking on what timers are enabled on a system is done with:
$ systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-07-21 15:08:22 EDT 12h left Mon 2020-07-20 14:05:40 EDT 12h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Wed 2020-07-22 00:00:00 EDT 21h left Tue 2020-07-21 00:00:01 EDT 2h 15min ago atop-rotate.timer atop-rotate.service Wed 2020-07-22 00:00:00 EDT 21h left Tue 2020-07-21 00:00:01 EDT 2h 15min ago man-db.timer man-db.service Wed 2020-07-22 00:00:00 EDT 21h left Tue 2020-07-21 00:00:01 EDT 2h 15min ago shadow.timer shadow.service 4 timers listed. Pass --all to see loaded but inactive timers, too.
A systemd .timer unit file is usually associated with a .service unit.
Let’s look at a timer unit file. The
man-db.timer is located at
/usr/lib/systemd/system/man-db.timer on my system.
# man-db.timer [Unit] Description=Daily man-db regeneration Documentation=man:mandb(8) [Timer] OnCalendar=daily AccuracySec=12h Persistent=true [Install] WantedBy=timers.target
WantedBy=timers.target and the time directives under
AccuracySec=. Systemd gives more directives
for controlling when a service runs.
man systemd.timers also online is the best
resource to find this out.
This unit file only describes when to run a job, but now how. That is
the purpose of a service file (
/usr/lib/systemd/system/man-db.service in this case).
# man-db.service [Unit] Description=Daily man-db regeneration Documentation=man:mandb(8) ConditionACPower=true [Service] Type=oneshot # Recover from deletion, per FHS. ExecStart=+/usr/bin/install -d -o root -g root -m 0755 /var/cache/man # Expunge old catman pages which have not been read in a week. ExecStart=/usr/bin/find /var/cache/man -type f -name *.gz -atime +6 -delete # Regenerate man database. ExecStart=/usr/bin/mandb --quiet User=root Nice=19 IOSchedulingClass=idle IOSchedulingPriority=7
Here we can see what the service is actually running with
some other details of how the processes get run, including
Nice=. This is a pretty common looking service file, the main difference
being that it gets run from a timer, and not at one of the boot time run
Cron or Systemd #
Well, I think there is a case for either one depending on the situation. If you already have a unit file made for your script/program, it would be rather easy to run it by creating and enabling a systemd timer.
Cron does have the advantage of simplicity and ease of use – although that is debatable depending on how you feel about the syntax. Not requiring the user to create extra files to schedule running things is very powerful, and all it requires is knowing the 5 *’s and some of the caveats of the crontab.