Cron Jobs Cheat Sheet
Crontab syntax, scheduling patterns, and management commands for automating recurring tasks on Linux systems.
Crontab Syntax
Five time fields plus the command to run.
# minute hour day-of-month month day-of-week command# * * * * *# | | | | |# | | | | +-- 0-7 (0 or 7 = Sunday)# | | | +-------- 1-12# | | +--------------------- 1-31# | +-------------------------- 0-23# +--------------------------------- 0-590 2 * * * /usr/local/bin/backup.sh # Every day at 2:00 AM*/15 * * * * /usr/local/bin/healthcheck.sh # Every 15 minutes0 9 * * 1-5 /usr/local/bin/report.sh # 9 AM on weekdays
Managing Crontabs
View, edit, and remove crontabs.
crontab -e # Edit current user's crontabcrontab -l # List current user's crontabcrontab -r # Remove current user's crontabcrontab -u alice -l # List another user's crontab (as root)sudo cat /etc/crontab # System-wide crontab (includes user field)ls /etc/cron.d/ # System job definitions dropped by packages
Special Strings & Notes
Shorthand macros and common gotchas.
- @reboot- Runs the command once at system startup
- @daily / @midnight- Equivalent to '0 0 * * *'
- @hourly- Equivalent to '0 * * * *'
- @weekly- Equivalent to '0 0 * * 0'
- MAILTO=- Set at the top of a crontab to email job output to an address
- PATH=- Cron runs with a minimal environment; set PATH explicitly if scripts rely on it
systemd Timers as a Cron Alternative
Replace crontab entries with unit-based scheduling that gets logging, dependencies, and jitter for free.
# /etc/systemd/system/backup.service[Unit]Description=Run backup script[Service]Type=oneshotExecStart=/usr/local/bin/backup.sh# /etc/systemd/system/backup.timer[Unit]Description=Daily backup at 02:00[Timer]OnCalendar=*-*-* 02:00:00RandomizedDelaySec=300Persistent=true[Install]WantedBy=timers.target
Managing systemd Timers
Enable timers and inspect their schedule and last-run status.
systemctl enable --now backup.timer # Enable and start the timer immediatelysystemctl list-timers --all # Show all timers with next/last run timessystemctl status backup.service # Check last run's exit code and logsjournalctl -u backup.service --since today # Full log output for the last runssystemctl start backup.service # Trigger the service manually, bypassing the timer
Preventing Overlapping Runs
Guard long-running jobs against a slow previous run still executing.
# Using flock so a second invocation exits instead of stacking up*/5 * * * * /usr/bin/flock -n /tmp/backup.lock /usr/local/bin/backup.sh# flock -n fails fast (non-blocking) if the lock is already held;# drop -n to have the second run wait instead of skip# Inside the script, an equivalent guard without flock:#!/usr/bin/env bashLOCKFILE=/tmp/backup.lockexec 200>"$LOCKFILE"flock -n 200 || { echo "already running"; exit 1; }# ...job body...
Step Values, Ranges & Lists
Combine cron field syntax for less common but precise schedules.
# Every 10 minutes between 8am-6pm on weekdays*/10 8-18 * * 1-5 /usr/local/bin/sync.sh# On the 1st and 15th of every month at 03:3030 3 1,15 * * /usr/local/bin/invoice.sh# Every other hour (0,2,4...22)0 */2 * * * /usr/local/bin/poll.sh# Last day of month workaround (cron has no 'L'): check in-script0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh
Cron Gotchas & Diagnostics
Common failure modes when a job 'works manually but not from cron'.
- % must be escaped- Unescaped '%' in a crontab command is treated as a newline; use '\%' or quote it
- Day-of-month AND day-of-week both set- Cron treats them as OR, not AND — '15 * * 1' runs both on the 15th and every Monday
- Timezone- Cron uses the system timezone by default; set CRON_TZ= per-line on modern cron implementations to override
- No login shell- ~/.bashrc and /etc/profile are not sourced, so aliases and shell functions are unavailable
- SHELL=- Set at the top of a crontab to force a specific shell (defaults to /bin/sh on many systems)
- anacron- Catches up on missed daily/weekly jobs after downtime (e.g. laptops); crontab has no such catch-up
- run-parts- Executes every script in a directory (e.g. /etc/cron.daily/) in filename order
Always redirect output explicitly (e.g. >> /var/log/job.log 2>&1) and use absolute paths in cron scripts — cron's minimal environment and lack of a terminal are the #1 cause of 'it works when I run it manually but not from cron' bugs.