Skip to main content
RunBook Academy

LinuxXXXVI Β· Scheduled Operationscron

cron syntax and anacron - the classic scheduler

Foundation⏱ ~10 mincrontabanacron

What you'll learn

  • Read and write crontab entries
  • Use anacron for missed jobs
  • Recognise common cron pitfalls
  • Migrate from cron to systemd timers

Prerequisites

Verified against Ubuntu 24.04 LTS Β· Debian 12 (Bookworm) Β· RHEL 9.x Β· Rocky Linux 9.x Β· AlmaLinux 9.x Β· Linux kernel 6.1 LTS / 6.6 LTS Β· systemd 255+ Β· OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) Β· nftables 1.0.x Β· chrony 4.x Β· Pacemaker 2.1.x Β· Corosync 3.1.x Β· 2026-08-09

Not yet marked complete on this device.

cron is the classic Unix scheduler. It runs commands at specified times. This lesson covers cron syntax, anacron for missed jobs, and common pitfalls.

Crontab syntax

# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ minute (0-59)
# β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ hour (0-23)
# β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ day of month (1-31)
# β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ month (1-12)
# β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ day of week (0-7, Sun = 0 or 7)
# β”‚ β”‚ β”‚ β”‚ β”‚
# * * * * * command

Examples:

# Every minute
* * * * * /usr/local/bin/health-check.sh

# Every 5 minutes
*/5 * * * * /usr/local/bin/health-check.sh

# Every day at 02:30
30 2 * * * /usr/local/bin/backup.sh

# Every Monday at 03:00
0 3 * * 1 /usr/local/bin/weekly-report.sh

# Every weekday at 18:00
0 18 * * 1-5 /usr/local/bin/eod-report.sh

Edit crontab

crontab -e    # edit current user\'s crontab
crontab -l    # list current user\'s crontab
crontab -r    # remove current user\'s crontab (CAREFUL)

Use crontab -e to edit, not direct editing of /var/spool/cron/.

System crontabs

For system-wide cron:

/etc/cron.d/       # per-service cron files
/etc/cron.daily/   # scripts run daily
/etc/cron.hourly/
/etc/cron.weekly/
/etc/cron.monthly/

Scripts in cron.daily/ and friends are executed by run-parts, driven from /etc/crontab β€” or, where anacron is installed (the default on Debian, Ubuntu and RHEL), driven by anacron instead, which is why they actually run at a randomised offset rather than at the fixed time /etc/crontab shows.

Environment

cron runs with a minimal environment:

  • PATH=/usr/bin:/bin (no /usr/sbin, no /usr/local/bin)
  • No ~/.bashrc or ~/.profile
  • HOME is the user’s home

Set explicit paths in cron:

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 2 * * * /usr/local/bin/backup.sh

Or in the script:

#!/usr/bin/env bash
export PATH=/usr/local/bin:/usr/bin:/bin

anacron

For laptops and machines that are not always on, anacron runs missed jobs:

# /etc/anacrontab
1       5       job-name      /path/to/script
7       10      weekly-job    /path/to/script

The first field is the period in days. The second is the delay in minutes after boot. Anacron catches up on missed runs after the machine starts.

For desktops and laptops, anacron is essential. For always- on servers, cron alone is sufficient.

Common pitfalls

  • PATH issues: cron uses minimal PATH. Always use absolute paths.
  • Missing environment: cron does not load shell rc files. Set environment in the crontab.
  • Output to nowhere: cron emails output if MAILTO is set; otherwise output is lost. Capture to a log file.
  • Long-running jobs: cron starts a new instance every interval. If a job takes longer than the interval, two instances run. Use flock for serialisation.
  • Errors hidden: cron does not show errors unless output is captured. Always log.
  • Day-of-month and day-of-week are ORed, not ANDed: if neither field is *, cron runs the job when either matches. 0 3 1 * 1 is not β€œthe 1st if it is a Monday” β€” it fires on the 1st of every month and on every Monday, about five times a month instead of once. man 5 crontab states it outright, and it is the single most common way a schedule silently runs too often.

To require both conditions, leave one field * and test inside the command. Note the escaped % β€” in a crontab, an unescaped % becomes a newline and everything after it is fed to the job on stdin:

# First of the month, but only when it is a Monday
0 3 1 * *  [ "$(date +\%u)" = 1 ] && /usr/local/bin/job.sh

A systemd timer expresses the same intent without the trap: OnCalendar=Mon *-*-01..07 03:00:00 for the first Monday of the month.

Knowledge check

Knowledge check Β· 3 questions

  1. Q1. What does `0 2 * * *` mean in a crontab?

  2. Q2. A cron job runs with a minimal environment and a PATH that usually excludes /usr/local/bin.

  3. Q3. Which of the following are valid cron patterns? Select all that apply.

Passing score: 75%. Answers are checked in this browser.