Skip to content

Task Scheduling

Linux provides three complementary tools for running tasks at specific times:

ToolWhen to use
cronRecurring tasks on always-on servers (runs at fixed times)
anacronRecurring tasks on desktops/laptops that may be off when cron fires
atOne-off tasks to run once at a specific future time
sleepDelay within a script or shell session

at schedules a command to run once at a specific future time. Jobs are run by the atd daemon.

Terminal window
# Basic scheduling
at 15:00 # at 3 PM today (or tomorrow if past)
at 15:00 2025-12-31 # at 3 PM on a specific date
at 'August 20 2025' # on a specific date
at '2:30 August 20 2025' # date and time
# Human-friendly time expressions
at noon
at midnight
at teatime # 4 PM
at tomorrow
at 'next Monday'
at 'now + 30 minutes'
at 'now + 3 hours'
at 'now + 3 days'
at 'now + 3 weeks'
at 'now + 3 months'

After typing at <time>, you enter an interactive prompt. Type your commands, then press CTRL-D to save:

Terminal window
at 15:00
warning: commands will be executed using /bin/sh
at> /usr/bin/touch /tmp/file_created_by_at
at> <CTRL-D>
job 20 at Wed Nov 17 15:00:00 2021
Terminal window
atq # list pending at jobs
# 20 Wed Nov 17 15:00:00 2021 a aaron
# (20 is the job ID)
at -c 20 # show what job 20 will run
atrm 20 # remove job 20

at obeys access control files:

  • /etc/at.allow - if this exists, only listed users can use at
  • /etc/at.deny - list of users denied access; if neither file exists, only root can use at

cron runs jobs on a recurring schedule. It is a daemon that wakes up every minute, checks all crontab files, and runs any jobs whose time has come.

  • cron is driven by crontab files - simple text files listing jobs and their schedules
  • There are system-wide crontab files (/etc/crontab, /etc/cron.d/) and per-user files (managed with crontab -e)
  • Each cron job is defined by a CRON expression (5 time fields) + the command to run
LimitationImpact
Minimum interval is 60 secondsCannot schedule sub-minute jobs
Centralized on one machineIf the machine is off, jobs are missed (no make-up)
No retry on failureA failed job won’t run again until next scheduled time
Misses jobs if machine is powered offSolution: use anacron instead

Forget the syntax? Run cat /etc/crontab to see an example with comments.

MIN HOUR DOM MON DOW CMD
0 2 * * * /usr/local/bin/backup.sh

crontab fields

FieldNameRange
1Minute0-59
2Hour0-23
3Day of Month1-31
4Month1-12 (or Jan-Dec)
5Day of Week0-6 (0=Sunday)
6CommandFull path recommended

Special characters:

SymbolMeaningExample
*Every value* * * * * = every minute
,Multiple values15,45 = at 15 and 45 minutes
-Range2-4 = hours 2, 3, 4
/Step*/4 = every 4 hours; 0-23/2 = every 2 hours
Terminal window
# Every minute
* * * * * /path/to/script.sh
# Every day at 2:30 AM
30 2 * * * /usr/local/bin/backup.sh
# Every Monday at 8 AM
0 8 * * 1 /usr/local/bin/weekly-report.sh
# 10th of every month at midnight
0 0 10 * * /usr/local/bin/monthly-cleanup.sh
# Every 15 minutes
*/15 * * * * /usr/local/bin/check-status.sh
# Weekdays only at 9 AM
0 9 * * 1-5 /usr/local/bin/workday-start.sh
# Full backup at 8:30 AM on June 10
30 8 10 6 * /home/sysadmin/full-backup

crontab example

Terminal window
crontab -e # edit your crontab (opens in $EDITOR)
crontab -l # list your crontab
crontab -r # remove your crontab (careful - no confirmation)
sudo crontab -l # view root's crontab
sudo crontab -e -u username # edit another user's crontab
sudo crontab -r -u username # remove another user's crontab

Drop scripts directly into these directories for automatic scheduling (no crontab syntax needed):

Terminal window
/etc/cron.hourly/ # run every hour
/etc/cron.daily/ # run every day
/etc/cron.weekly/ # run every week
/etc/cron.monthly/ # run every month
Terminal window
# Add a script to run hourly
sudo cp myscript.sh /etc/cron.hourly/
sudo chmod +rx /etc/cron.hourly/myscript.sh
# Remove it
sudo rm /etc/cron.hourly/myscript.sh

Scripts in these directories must be executable and should not have a file extension (or have at most a .sh extension).


anacron executes jobs periodically (in days), but unlike cron, it checks timestamps to see if a job was missed. If the machine was off when a job was due, anacron runs it at the next boot (after a configurable delay).

This makes anacron ideal for desktops, laptops, and any machine that isn’t guaranteed to be running 24/7.

  1. On startup (or periodically), anacron reads /etc/anacrontab
  2. For each job, it checks the timestamp in /var/spool/anacron/
  3. If current_date - last_run >= period_in_days, it runs the job
  4. A configurable delay in minutes staggers jobs to avoid all running at once immediately after boot

Key files:

  • /etc/anacrontab - job definitions
  • /var/spool/anacron/ - timestamp files per job (daily, weekly, monthly, etc.)
period_days delay_minutes job-identifier command
Terminal window
sudo vim /etc/anacrontab
# Run every 3 days, 10 min delay after boot
3 10 test-job /usr/bin/touch /root/anacron_was_here
# Weekly
7 10 weekly-job /usr/local/bin/weekly.sh
@weekly 10 weekly-job /usr/local/bin/weekly.sh # same thing
# Monthly
@monthly 15 monthly-job /usr/local/bin/monthly.sh

The delay (in minutes) is important: if a machine was off and missed 5 scheduled daily jobs, running all 5 simultaneously at boot would cause a CPU/I/O spike. The delay staggers them.

Terminal window
anacron -T # test syntax (no output = valid)
# anacron: Invalid syntax in /etc/anacrontab on line 17 - skipping this line
FlagEffect
-fForce run all jobs, ignoring timestamps
-nRun now - ignore delay
-sSerialize: don’t start a new job until previous one finishes
-uUpdate timestamps only; don’t actually run jobs
-dDebug mode: don’t fork, print messages to stderr
-TTest syntax of /etc/anacrontab

Featurecronanacron
TypeDaemon (runs continuously)Not a daemon (runs on trigger)
Best forServers (always on)Desktops / laptops
Minimum interval1 minute1 day
Misses jobs when off?Yes - they are lostNo - runs them on next boot
Who can use itRoot and regular usersRoot only (unless specially configured)
Retry on failureNoNo

Rule of thumb:

  • Server that’s always on: use cron
  • Workstation or laptop that sleeps/hibernates: use anacron (or systemd timers)

sleep suspends execution for a specified duration. After the time elapses (or a signal is received), execution resumes.

Terminal window
sleep 5 # wait 5 seconds
sleep 5s # same (explicit suffix)
sleep 2m # 2 minutes
sleep 1h # 1 hour
sleep 1d # 1 day
sleep 0.5 # 500 milliseconds

Suffixes: s (seconds, default), m (minutes), h (hours), d (days).

Terminal window
# Retry loop with backoff
for i in 1 2 3 4 5; do
command && break # run command; if success, exit loop
echo "Attempt $i failed, waiting 10s..."
sleep 10
done
# Wait for a service to come up
until systemctl is-active --quiet nginx; do
echo "Waiting for nginx..."
sleep 2
done
# Throttle a loop (avoid hammering an API)
for item in "${items[@]}"; do
process "$item"
sleep 1
done

All cron, anacron, and at jobs are logged:

Terminal window
# All cron job activity
sudo cat /var/log/cron
journalctl -u crond --since "yesterday"
# Filter by type
sudo grep CMD /var/log/cron # cron jobs that ran
sudo grep anacron /var/log/cron # anacron activity
sudo grep atd /var/log/cron # at jobs that ran
# Check for failed jobs (non-zero exit)
journalctl -u crond -p warning

Output from cron jobs is typically mailed to the crontab owner. To redirect it:

Terminal window
# Discard all output
* * * * * /path/to/script.sh > /dev/null 2>&1
# Log stdout and stderr to a file
* * * * * /path/to/script.sh >> /var/log/myscript.log 2>&1
# Log with timestamp
* * * * * date >> /tmp/job.log && /path/to/script.sh >> /tmp/job.log 2>&1