Job Scheduling: cron, at, and systemd Timers

Job Scheduling: cron, at, and systemd Timers

What You Will Achieve

  • Read and write the crontab syntax (five fields)
  • Explain the difference between a user crontab and a system crontab (/etc/crontab, /etc/cron.d)
  • Manage one-shot jobs with at / atq / atrm
  • Decide when to use anacron and systemd timer
  • Understand execution control with cron.allow / cron.deny
  • Avoid exam-frequent pitfalls such as the "cron PATH problem"

This is the core of LPIC-1 objective 107.2 "Automate system administration tasks by scheduling jobs". It is the technique to run routine work, such as regular backups and log rotation, automatically without human intervention.

Which Scheduler Should You Use?

For repeated runs choose cron, for a single run choose at, for periodic runs on a machine that is not always on choose anacron, and for advanced control in a systemd environment choose systemd timer. That is the starting point for the decision.

Requirement Tool Key command / file
Periodic, e.g. daily or hourly cron crontab -e, /etc/crontab
Once at a given time at at, atq, atrm
Avoid misses when the machine was off anacron /etc/anacrontab
systemd integration, deps, log linkage systemd timer .timer + .service, OnCalendar=

cron skips a job if the machine is not running at the scheduled time. By contrast, anacron judges by "days elapsed since the last run", so it does not miss runs even on environments like laptops that are powered off for stretches of time.

The crontab Syntax

Each crontab line consists of six elements: "minute hour day month weekday command". The first five are time fields, and the sixth onward is the command to run.

# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 7, 0 and 7 are Sunday)
# │ │ │ │ │
# * * * * * command to run

The special characters usable in each field are as follows.

Symbol Meaning Example
* All values * in minute means every minute
, List of values 0,30 means minute 0 and minute 30
- Range 1-5 (weekday) means Mon-Fri
/ Interval (step) */15 (minute) means every 15 minutes

Let us read some concrete examples.

*/15 * * * *   /usr/local/bin/check.sh      every 15 minutes
0 3 * * *      /usr/local/bin/backup.sh     daily at 3:00
0 9 * * 1-5    /usr/local/bin/report.sh     weekdays (Mon-Fri) at 9:00
0 0 1 * *      /usr/local/bin/monthly.sh    1st of every month at 0:00

On Vixie-style cron you can also use nicknames such as @reboot, @daily, @hourly, @weekly, @monthly, @yearly (@annually), and @midnight. @reboot runs once when the cron daemon starts.

In the weekday field, both 0 and 7 mean Sunday. The exam often asks about the "weekday number". Remember: 0 = Sunday, 1 = Monday ... 6 = Saturday, and 7 is also Sunday.

Steps

Step 1: Edit the user crontab

crontab -e
crontab: installing new crontab

crontab -e opens an editor (set by $EDITOR / $VISUAL) and edits the current user's crontab. On save it is stored under /var/spool/cron/ (or crontabs/ depending on the distribution) and the cron daemon picks it up automatically. Adding the following line runs the backup daily at 3:00.

0 3 * * * /usr/local/bin/backup.sh

Step 2: List and remove entries

crontab -l
crontab -r
0 3 * * * /usr/local/bin/backup.sh

crontab -l shows the registered entries and crontab -r removes the entire crontab. Since -r deletes without confirmation, it is safer to keep a copy with -l first. As root you can operate another user's crontab with crontab -u user -e / -u user -l.

crontab -r and crontab -e are adjacent keys; hitting -r by mistake wipes all entries. It is a good idea to back up with crontab -l > ~/crontab.bak.

Step 3: Use the system crontab

cat /etc/crontab
ls /etc/cron.d/
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly

/etc/crontab and files under /etc/cron.d/ are the system crontab. Unlike a user crontab, they insert a "run-as user" field between the time fields and the command (a six-field layout). This difference is exam-frequent. Placing scripts in /etc/cron.{hourly,daily,weekly,monthly} runs them at each interval via run-parts.

Step 4: Run once with at

at 22:00 tomorrow
atq
atrm 2
warning: commands will be executed using /bin/sh
at> /usr/local/bin/deploy.sh
at> <EOT>
job 2 at Sat May 31 22:00:00 2026
2	Sat May 31 22:00:00 2026 a user

at runs a job once at a specified time. Enter the command at the prompt and confirm with Ctrl+D (<EOT>). Check the queue with atq (= at -l) and delete job number 2 with atrm 2 (= at -d 2). The time can be given flexibly, such as 10:00, now + 1 hour, midnight, or teatime (16:00). batch differs from at in that it runs the job when system load drops.

Step 5: Control who may schedule jobs

cat /etc/cron.allow
cat /etc/cron.deny
alice
bob

cron usage is controlled by /etc/cron.allow and /etc/cron.deny. The decision rules are as follows.

State of the files Result
cron.allow exists Only users listed there are allowed
No cron.allow, cron.deny exists All users except those in cron.deny
Neither file exists Implementation-dependent (often root only)

at is controlled the same way with /etc/at.allow / /etc/at.deny. The *.allow file takes precedence: when it exists, *.deny is not consulted.

anacron vs systemd timer

cron runs on the assumption that the power is on, but anacron and systemd timer can make up for runs missed while powered off. If the host is not an always-on server, these two are worth considering.

anacron

/etc/anacrontab has a different syntax from cron: it uses four fields, "period (days), delay (minutes), job-identifier, command".

cat /etc/anacrontab
# period  delay  job-identifier  command
1	5	cron.daily	run-parts --report /etc/cron.daily
7	25	cron.weekly	run-parts --report /etc/cron.weekly
@monthly 45	cron.monthly	run-parts --report /etc/cron.monthly

The first field is "how many days between runs" and the second is "the delay (minutes) to wait after startup". anacron cannot specify minutes or a clock time; it only has day-level granularity. It records the last run date under /var/spool/anacron/ and, if the interval has been exceeded, runs the job at startup.

systemd timer

A systemd timer consists of two units: a .timer unit and a .service unit that does the actual work.

systemctl list-timers
NEXT                        LEFT     LAST                        PASSED   UNIT             ACTIVATES
Sat 2026-05-31 03:00:00 UTC 8h left  Fri 2026-05-30 03:00:00 UTC 15h ago  backup.timer     backup.service

OnCalendar= inside the .timer specifies the run time. OnCalendar=*-*-* 03:00:00 means daily at 3:00, and OnCalendar=daily is equivalent. systemctl list-timers lists the next run, last run, and the associated service. The strength of systemd timer is that it handles dependency control, log inspection via journalctl, and catch-up runs (like anacron) with Persistent=true.

You can verify that an OnCalendar= expression is valid with systemd-analyze calendar "Mon *-*-* 09:00:00". It prints the next trigger time.

Common Mistakes and Fixes

Symptom: A script that "should" run under cron does not (PATH problem)

Cause: The PATH of the shell cron starts is shorter than an interactive login shell, and /etc/profile and .bashrc are not sourced. The command is not found and fails

Check:

* * * * * env > /tmp/cron-env.txt

Fix: Write commands with an absolute path (/usr/local/bin/backup, not backup). Or set PATH=... explicitly at the top of the crontab.

Symptom: Environment variables in the script are unset and it misbehaves

Cause: cron does not inherit the interactive shell's environment variables (custom settings around LANG, HOME, etc.). A script that assumes a login shell behaves unexpectedly

Check:

crontab -l

Fix: Set the needed variables explicitly in the script, or write LANG=ja_JP.UTF-8 and the like in the crontab. To reproduce a login environment, start it with bash -lc 'command'.

Symptom: Everything after % in the crontab is ignored / the command is cut off

Cause: In a crontab, a % on the command line is treated specially as a newline (a separator for standard input)

Check:

crontab -l

Fix: To pass a literal %, escape it with a backslash as \%. Write it like date +\%Y\%m\%d.

Symptom: A job specifying both weekday and day-of-month runs on unexpected days

Cause: In cron, when both "day of month" and "day of week" are set to something other than *, the job runs on a day where either matches (OR, not AND)

Check:

crontab -l

Fix: Understand that OR behavior is by design. For an AND condition like "a specific weekday and a specific date", check the date inside the command and control it there.

Symptom: cron output (errors) is invisible

Cause: cron mails a job's standard output and standard error locally to the user. You will not notice unless you read that mail

Check:

grep CRON /var/log/syslog

Fix: Redirect output to a file (>> /var/log/myjob.log 2>&1). Writing MAILTO=address in the crontab changes the recipient, and MAILTO="" suppresses mail.

Completion Checklist

  • [ ] Edited the user crontab with crontab -e and verified with crontab -l
  • [ ] Confirmed the system crontab (/etc/crontab) has a user field
  • [ ] Registered a job with at and managed it with atq / atrm
  • [ ] Understood the precedence of /etc/cron.allow / cron.deny
  • [ ] Wrote scripts with absolute paths to avoid the PATH problem
  • [ ] Checked systemd timers with systemctl list-timers

Summary

Scenario Command / file Purpose
Periodic (user) crontab -e Register your own periodic jobs
Periodic (system) /etc/crontab, /etc/cron.d Periodic jobs with a user field
Once at, atq, atrm One-shot run at a given time
Miss prevention /etc/anacrontab Daily work on non-always-on hosts
systemd integration .timer + OnCalendar= Deps, logs, Persistent runs
Execution control cron.allow / cron.deny Per-user allow / deny

Job scheduling is the foundation for automating system operations. Combined with log management and process priority, it raises the reliability of unattended operation by another level.

Next Reading

Continue Your LPIC-1 Journey

LPIC-1 Hub

  • LPIC-1 Learning Hub — Full LPIC-1 article map, progress tracking, and exam objective coverage

Practice