Job Scheduling on Linux
Scheduling jobs to happen at a later time on a Linux based machine can be somewhat confusing. Confused by 5 4 8-10/4 6/4 *
baffled by 5 */4 * * *
? All will be revealed!
cron
Scheduling jobs on a Linux machine can be done in several ways. Let's start with cron
- the primary program that orchestrates the whole proceeding. Its name comes from the Greek word Chronos, which means time. By filling in a crontab (read cron-table), you can tell it what to do when. It's essentially a time-table of jobs you'd like it to run.
Your Linux machine should come with cron installed already. You can check if cron is installed and running by entering this command into your terminal:
if [[ "$(pgrep -c cron)" -gt 0 ]]; then echo "Cron is installed :D"; else echo "Cron is not installed :-("; fi
If it isn't installed or running, then you'll have to investigate why this isn't the case. The most common is that it isn't installed. It's normally in the official repositories for most distributions - on Debian-based system sudo apt install cron
should suffice. Arch-based users may need to check to make sure that the system service is enabled and do so manually.
With cron setup and ready to go, we can start adding jobs to it. This is done by way of a crontab
, as explained above. Each user has their own crontab such that they can each configure their own individual sets jobs. To edit it, type this:
crontab -e
This will open your favourite editor with your crontab ready for editing (if you'd like to change your editor, do sudo update-alternatives --config editor
or change the EDITOR
environment variable). You should see a bunch of lines like this:
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
I'd advise you keep this for future reference - just in case you find yourself in a pinch later - so scroll down to the bottom and start adding your jobs there.
Let's look at the syntax for telling cron about a job next. This is best done by example:
0 1 * * 7 cd /root && /root/run-backup
This job, as you might have guessed, runs a custom backup script. It's one I wrote myself, but that's a story for another time (comment below if you'd like me to post about that). What we're interested in is the bit at the beginning: 0 1 * * 7
. Scheduling a cron job is done by specifying 5 space-separated values. In the case of the above, the job will run at 1am every Sunday morning. The order is as follows:
- Minute
- Hour
- Day of the Month
- Month
- Day of the week
For of these values, a number of different specifiers can be used. For example, specifying an asterisk (*
) will cause the job to run at every interval of that column - e.g. every minute or every hour. If you want to run something on every minute of the day (such as a logging or monitoring script), use * * * * *
. Be aware of the system resources you can use up by doing that though!
Specifying number will restrict it to a specific time in an interval. For example, 10 * * * *
will run the job at 10 minutes past every hour, and 22 3 * * *
will run a job at 03:22
in the morning every day (I find such times great for maintenance jobs).
Sometimes, every hour or every minute is too often. Cron can handle this too! For example 3 */2 * * *
will run a job at 3 minutes past every second hour. You can alter this at your leisure: The value after the forward slash (/
) decides the interval (i.e. */3
would be every third, */15
would be every 15th, etc.).
The last column, the day of the week, is an alternative to the day of the month column. It lets you specify, as you may assume, the day oft he week a job should run on. This can be specified in 2 way: With the numbers 0-6, or with 3-letter short codes such as MON
or SAT
. For example, 6 20 * * WED
runs at 6 minutes past 8 in the evening on Wednesday, and 0 */4 * * 0
runs every 4th hour on a Sunday.
The combinations are endless! Since it can be a bit confusing combining all the options to get what you want, crontab.guru is great for piecing cron-job specifications together. It describes your cron-job spec in plain English for you as you type!
(Above: crontab.guru displaying a random cronjob spec)
What if I turn my computer off?
Ok, so cron is all very well, but what if you turn your machine off? Well, if cron isn't running at the time a job should be run, then it won't get executed. For those of us who don't leave their laptops on all the time, all is not lost! It's time to introduce the second piece of software at our disposal.
Enter stage left: anacron. Built to be a complement to cron, anacron sets up 3 folders:
/etc/cron.daily
/etc/cron.weekly
- /etc/cron.monthly`
Any executable scripts in this folder will be run at daily, weekly, and monthly intervals respectively by anacron, and it respects the hash-bang (that #!
line at the beginning of the script) too!
Most server systems do not come with anacron pre-installed, though it should be present if your distributions official repositories. Once you've installed it, edit root
's crontab (with sudo crontab -e
if you can't remember how) and add a job that executes anacron every hour like so:
# Run anacron every hour
5 * * * * /usr/sbin/anacron
This is important, as anacron does not in itself run all the time like cron does (this behaviour is called a daemon in the Linux world) - it needs a helping hand to get it to run.
If you've got more specific requirements, then anacron also has it's own configuration file you can edit. It's found at /etc/anacrontab
, and has a different syntax. In the anacron table, jobs follow the following pattern:
- period - The interval, in days, that the job should run
- delay - The offset, in minutes, that the job should run at
- job identifier - A textual identifier (without spaces, of course) that identifies the job
- command - The command that should be executed
You'll notice that there are 3 jobs specified already - one for each of the 3 folders mentioned above. You can specify your own jobs too. Here's an example:`
# Do the weekly backup
7 20 run-backup cd /root/data-shape-backup && ./do-backup;
The above job runs every 7 days, with an offset of 20 minutes. Note that I've included a command (the line starting with a hash #
) to remind myself as to what the job does - I'd recommend you always include such a comment for your own reference - whether you're using cron, anacron, or otherwise.
I'd also recommend that you test your anacron configuration file after editing it to ensure it's valid. This is done like so:
anacron -T
I'm not an administrator, can I still use this?
Sure you can! If you've got anacron installed (you could even compile it from source locally if you haven't) and want to specify some jobs for your local account, then that's easily done too. Just create an anacrontab
file anywhere you please, and then in your regular crontab (crontab -e
), tell anacron
where you put it like this:
# Run anacron every hour
5 * * * * /usr/sbin/anacron -t "path/to/anacrontab"
What about one-off jobs?
Good point. cron and anacron are great for repeating jobs, but what if you want to set up a one-off job to auto-disable your firewall before enabling it just in case you accidentally lock yourself out? Thankfully, there's even an answer for this use-case too: atd
.
atd is similar to cron
in that it runs a daemon in the background, but instead of executing jobs specified in a crontab
, you tell it when you want it to execute a series of commands, and then enter the commands themselves. For example:
$ at now + 10 minutes
warning: commands will be executed using /bin/sh
at> echo -e "Testing"
at> uptime
at> <EOT>
job 4 at Thu Jul 12 14:36:00 2018
In the above, I tell it to run the job 10 minutes from now, and enter a pair of commands. To end the command list, I hit CTRL + D on an empty line. The output of the job will be emailed to me automatically if I've got that set up (cron and anacron also do this).
Specifying a time can be somewhat fiddly, but its also quite flexible:
at tomorrow
at now + 5 hours
at 16:06
at next month
at 2018 09 25
....and so on. Listing the current scheduled jobs is also just as easy:
atq
This will output a list of scheduled jobs that haven't been run yet. You can't see any jobs that aren't created by you unless you're root (use sudo
), though. You can use the job ids listed here to cancel a job too:
# Remove job id 4:
atrm 4
Conclusion
That just about concludes this whirlwind tour of job scheduling on Linux systems. We've looked at how to schedule jobs with cron, and how to ensure our jobs get run - even when the target machine isn't turned on all the time with anacron. We've also looked at one-time jobs with atd, and how to manage the job queue.
As usual, this is a starting point - not an ending point! Job scheduling is just the beginning. From here, you can look at setting up automated backups. You could investigate setting up an email server, and how that integrates with cron. You can utilise cron to perform maintenance for your next great web (or other!) application. The possibilities are endless!
Found this useful? Still confused? Comment below!