Linux7 min read

Cron jobs: scheduling tasks on Linux

linuxcronschedulingautomation

Cron is the built-in job scheduler on Linux. It runs commands at specified times and intervals. Backups, log rotation, cleanup scripts, health checks — if something needs to happen regularly, cron is usually the answer.

The crontab format

Each user has a crontab (cron table). Edit it:

crontab -e

Each line has six fields:

*    *    *    *    *    command to execute
|    |    |    |    |
|    |    |    |    +----- day of week (0-6, Sunday=0)
|    |    |    +---------- month (1-12)
|    |    +--------------- day of month (1-31)
|    +-------------------- hour (0-23)
+------------------------- minute (0-59)

Some examples:

0 2 * * * /opt/scripts/backup.sh          # every day at 2:00 AM
*/5 * * * * /opt/scripts/check.sh          # every 5 minutes
0 9 * * 1 /opt/scripts/weekly-report.sh    # every Monday at 9 AM
0 0 1 * * /opt/scripts/monthly-cleanup.sh  # first of every month at midnight

Special strings

Instead of the five time fields, you can use:

  • @reboot — run once at startup
  • @daily or @midnight — once a day
  • @hourly — once an hour
  • @weekly — once a week
  • @monthly — once a month
@reboot /opt/scripts/start-services.sh
@daily /opt/scripts/log-rotate.sh

Environment gotcha

Cron runs with a minimal environment. Your PATH is usually just /usr/bin:/bin. This means commands that work fine in your terminal might fail in cron because they’re not on that path.

Fix it by setting PATH at the top of your crontab:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0 2 * * * /opt/scripts/backup.sh

Or use absolute paths in your commands. I prefer absolute paths because they’re explicit about what runs.

Output and logging

Cron sends any output (stdout and stderr) via email to the crontab owner. On most systems, this email goes nowhere useful because local mail isn’t configured.

Redirect output explicitly:

0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

Or suppress it if you don’t care:

0 2 * * * /opt/scripts/backup.sh > /dev/null 2>&1

Suppressing all output is risky. If the script fails, you’ll never know. Better to log to a file and set up log rotation for it.

System crontabs

In addition to user crontabs, there are system-wide cron directories:

/etc/cron.d/           # drop-in cron files
/etc/cron.daily/       # scripts run once daily
/etc/cron.hourly/      # scripts run once hourly
/etc/cron.weekly/      # scripts run once weekly
/etc/cron.monthly/     # scripts run once monthly

Scripts in these directories don’t need a crontab entry. They’re run by the system’s cron daemon (usually anacron on Ubuntu, which handles systems that aren’t always running).

Common pitfalls

The percent sign. In cron, % is treated as a newline. If your command has % in it (like a date format), escape it with a backslash: \%.

# Wrong — the % breaks the command
0 2 * * * date +%Y-%m-%d > /tmp/date.txt

# Right
0 2 * * * date +\%Y-\%m-\%d > /tmp/date.txt

Missing newlines. The last line in a crontab must end with a newline. If it doesn’t, cron silently ignores that line. This has bitten me more than once.

Running as the wrong user. crontab -e edits your own crontab. To edit another user’s crontab: crontab -u username -e. System services should run from the service account’s crontab, not root’s.

Race conditions. If a cron job takes longer than the interval between runs, you’ll have overlapping executions. Use a lockfile:

#!/bin/bash
exec 200>/var/lock/myjob.lock
flock -n 200 || exit 1
# rest of script

Testing cron entries

Before trusting a cron job, test the command manually first. Then check /var/log/syslog for cron entries:

grep CRON /var/log/syslog

This shows when cron started your job and whether there were errors.

When to use something else

Cron is fine for simple schedules. For more complex needs:

  • systemd timers — better logging, dependency management, and calendar events
  • at — one-time scheduled jobs
  • anacron — handles machines that aren’t always on

Remarks

Cron has been around since the 1970s and it still works. The syntax is cryptic at first but becomes natural. The main things to remember: use absolute paths, handle output, and end the file with a newline. When a cron job doesn’t run, check syslog first.

Related Posts