Cron Cheatsheet - Expressions & Task Scheduling
Writing a cron expression is easy; the hard part is an expression that looks correct and still never fires, and the causes are usually not in the expression at all. The sheet moves from the five-field format through special characters and macros such as @daily, then covers common examples and the three groups that decide whether the job actually runs: environment, permissions and troubleshooting. The traps are named explicitly: cron gets a nearly empty environment rather than your shell, so a missing PATH is fixed by exporting it inside the script or using absolute paths, especially for interpreters. Output defaults to local mail, and with no MTA configured the failure message evaporates, so redirect with 2>&1 into a log. And when day-of-month and day-of-week are both constrained the relationship is OR, not AND. Troubleshooting order is simple: check whether a log line appeared at all — no log means an environment or permission problem, an empty log usually means a bad path or redirection.
Typical Use Case
For developers who need scripts to run on a schedule. Almost every crontab failure comes from two environment differences rather than syntax: cron's PATH is minimal, so 'it runs manually but not on schedule', and output goes to mail by default, which fills a mailbox or disk. This sheet is organised as expression, special symbols, time macros, environment variables, permissions, and troubleshooting, putting both traps up front and giving a minimal reproducible test case for debugging.
Expression Format 6
* * * * * commandMinute (0-59)Hour (0-23)Day of month (1-31)Month (1-12)Day of week (0-7)Special Characters 6
**/NA-BA,B,CA-B/NLSpecial Time Macros 6
@reboot@yearly / @annually@monthly@weekly@daily / @midnight@hourlyCommon Examples 8
*/5 * * * * command0 * * * * command0 3 * * * command0 0 * * 1 command0 0 1 * * command0 9-18 * * 1-5 command*/30 * * * * command0 0 * * 0 commandEnvironment Variables 6
SHELL=/bin/bashPATH=/usr/local/bin:/usr/bin:/binMAILTO="admin@example.com"MAILTO=""HOME=/home/userCRON_TZ=Asia/ShanghaiPermissions 6
/etc/cron.allow/etc/cron.denycrontab -u user -ecrontab -u user -lcrontab -u user -rls /var/spool/cron/Troubleshooting 5
grep CRON /var/log/sysloggrep cron /var/log/cronsystemctl status crondsystemctl status cron* * * * * echo test >> /tmp/cron-test 2>&1Parameter matrix
| Parameter | Effect | Example |
|---|---|---|
Minute (0-59) | Which minute within the hour | 0 * * * * command |
Hour (0-23) | Which hour of the day; 0 is midnight | 0 3 * * * command |
Day of month (1-31) | Which day of the month | 0 0 1 * * command |
Month (1-12) | Which month of the year | 0 0 * 6 * command |
Day of week (0-7) | Both 0 and 7 mean Sunday | 0 0 * * 1 command |
*/N | Once every N units | */5 * * * * command |
A-B/N | Step within a range | 0 9-18 * * 1-5 command |
A,B,C | Enumerate multiple values | 0 0 * * 1,3,5 |
@reboot | Run once at boot | @reboot /opt/start.sh |
@daily / @hourly | Once a day / once an hour | @daily /opt/backup.sh |
PATH=... | The default cron PATH is minimal, so it must be declared explicitly | PATH=/usr/local/bin:/usr/bin:/bin |
MAILTO="" | Disable email notifications | MAILTO="" |
CRON_TZ | Specify the timezone | CRON_TZ=Asia/Shanghai |
Redirect | Write output to a log instead of mailing it | command >> /var/log/job.log 2>&1 |
Common pitfalls
SymptomThe script runs fine by hand, but in crontab it reports command not found.
CauseThe cron PATH is extremely minimal and does not include paths configured in interactive shells such as /usr/local/bin.
FixWrite PATH=... explicitly at the top of the crontab, or use absolute paths everywhere.
SymptomA node / python script fails partway through when run by cron.
CauseEnvironments like conda / pyenv / nvm rely on interactive shell init scripts to set PATH, which are not loaded under cron.
FixWrite the full absolute path of the interpreter at the start of the script, or source the corresponding init script directly in the crontab.
SymptomThe job produces no output and you cannot see why it failed.
Causecron mails the output, but servers often have no MTA configured, so the output is dropped.
FixRedirect explicitly: command >> /var/log/job.log 2>&1, or set MAILTO="" to disable notifications.
SymptomThe log directory fills up, or the mailbox is stuffed with duplicate content.
CauseOutput accumulates over time with no rotation.
FixRedirect to a log file and configure logrotate; when output is not needed use command > /dev/null 2>&1.
SymptomYou thought it would run on a certain day, but it did not.
CauseWhen both day-of-month and day-of-week are set it is an OR not an AND, and days that do not exist in the month are skipped.
FixRemember 0 0 13 * 5 means the 13th or every Friday; days the month does not have will not run, so use L or the previous day as a fallback.
SymptomRelative paths written in cron cannot find the file.
CauseThe cron working directory is the HOME directory of the user, not the directory you changed into.
FixUse cd /opt/app && command in the crontab, or write everything as absolute paths.
SymptomAfter deleting a user crontab you want to restore it, but there is no backup.
Causecrontab -u user -r deletes all entries directly with no prompt.
FixBefore deleting, back up with crontab -u user -l > /root/crontab-user.bak; editing entries one by one with crontab -e is safer.
SymptomAfter editing the crontab it has no effect.
CauseIn most cases saving takes effect immediately, but the script itself lacks execute permission.
FixNo need to restart the cron service; make sure the script is chmod +x, or invoke it explicitly with bash /path/script.sh in the command.
Troubleshooting
1First verify cron works with a minimal job
* * * * * echo test >> /tmp/cron-test 2>&1Wait a minute and see whether the file has content; if it does, cron is fine and the problem is in your original script.
2Check whether the cron service is running
systemctl status crond || systemctl status cronOn RHEL/CentOS it is crond, on Debian/Ubuntu it is cron; the names differ.
3View the cron execution log
grep CRON /var/log/syslogDebian/Ubuntu logs to syslog; on RHEL/CentOS switch to grep cron /var/log/cron.
4View the current user crontab
crontab -lAs root, use crontab -u user -l / -e to edit another user; remember to back up before deleting.
5Confirm the absolute paths of interpreter scripts
which node && which python3Write the printed absolute paths into the crontab to sidestep PATH and environment init problems.
Command Examples
Debug: confirm the cron chain works at all
* * * * * echo "cron ok $(date)" >> /tmp/cron-test 2>&1Do not start by debugging your real job. First use this line to write a row every minute and redirect stderr: the most common cron failure is a too-minimal PATH causing commands not to be found, and without redirecting output you see nothing. Once the chain works, switch to the real job.
Run at 3am daily with logging
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1You must declare SHELL and PATH explicitly at the top, otherwise the minimal cron environment makes the script unable to find commands via relative paths. Redirecting logs to a file is easier to review later than mailing them via MAILTO.
Prefer a systemd timer instead
systemctl list-timersThe systemd timer shows the next and last run times, which is far more direct than digging through logs to answer whether the job ran; output also goes to journalctl instead of email.
Output
NEXT LEFT LAST PASSED UNIT Tue 2026-09-30 03:00:00 CST 9h left Mon 2026-09-29 03:00:00 CST 23h ago backup.timer Tue 2026-09-30 00:30:00 CST 6h left Mon 2026-09-29 00:30:00 CST 23h ago logrotate.timer
Common Pitfalls
- cron has a very lean default PATH, so commands that work in your shell are often not found; always declare PATH and SHELL at the top of the crontab.
- Without output redirection errors go to MAILTO or vanish; always append >> log 2>&1.
- When both the day-of-month and day-of-week fields are *, the job fires on days matching either, which means some months run it twice — verify the expected count.
- crontab -u user -r removes all of a user's jobs with no confirmation; export a backup with -l first.
- cron does not check whether the command exists, so a bad path or shebang makes the job fail silently.
Tips
- cron's environment differs from an interactive shell; PATH may be incomplete — export PATH explicitly in your script if commands aren't found.
- Use absolute paths for commands to avoid cron failing to locate executables, especially interpreters like node or python.
- Output is emailed by default; redirect to a log file instead, e.g. command >> /var/log/job.log 2>&1.
- Specifying both day-of-month and day-of-week is an OR relationship, not AND — 0 0 13 * 5 means the 13th OR every Friday.
- No need to restart cron after editing crontab; changes take effect on save, but the script itself must be executable.
- If both cron.allow and cron.deny exist, only allow is consulted; if neither exists, the distribution's default policy applies.
FAQ
My cron entry looks correct but never runs — what is the likely cause?
Nine times out of ten it is the minimal default PATH: cron has a very lean environment, so a command that works in your shell cannot be found. Declare PATH and SHELL explicitly at the top of the crontab. Second most common: no output redirection, so errors vanish. To debug, add * * * * * echo test >> /tmp/cron-test 2>&1 to write a line each minute, confirm the chain works, then convert to the real job.
What do 0 0 * * 1 and 0 0 1 * * mean?
The five fields are minute, hour, day-of-month, month, day-of-week. So 0 0 * * 1 runs at midnight every Monday, while 0 0 1 * * runs at midnight on the first of every month. The classic trap is overlapping the day-of-month and day-of-week fields — when both match, the job can fire twice in some months, so check your expected run count.
Why do environment variables differ inside cron?
That is expected — cron runs with a minimal environment and does not read your .bashrc or .zshrc. Declare what you need at the top of the crontab, for example PATH=/usr/local/bin:/usr/bin:/bin and SHELL=/bin/bash. Also set MAILTO="" to silence mail, otherwise frequent jobs will flood your inbox.
How do I confirm a cron job is actually firing?
Check cron's own log first: grep CRON /var/log/syslog on Debian/Ubuntu, grep cron /var/log/cron on RHEL/CentOS. Then confirm the service is running with systemctl status cron (or crond). If the task is a systemd timer instead, use systemctl list-timers to see whether it is enabled and when it next runs.
What is the difference between */5 and 0 * * * * in frequency?
*/5 * * * * means "every 5 units starting from 0", which in the minute field means every five minutes. 0 * * * * pins the minute to 0, so it fires at the top of every hour. Both are common for log rotation and scheduled backups — and remember to set PATH alongside them, or relative commands inside the script will fail.
Official References
Each command links to its official documentation below, so you can verify the latest usage and read deeper.
Maintained by LaoHand
Publicly updated on Jul 21, 2026, continuously proofread against official docs.
Contact Us
Wrong command or description? Send us corrections, business inquiries or product feedback by email.
Contact Us