Cron Cheatsheet - Cron Expression & Task Scheduling Reference
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 |
|---|---|---|
分钟(0-59) | 每小时内的第几分钟 | 0 * * * * command |
小时(0-23) | 一天内第几小时,0 为午夜 | 0 3 * * * command |
日(1-31) | 每月第几天 | 0 0 1 * * command |
月(1-12) | 每年第几月 | 0 0 * 6 * command |
周(0-7) | 0 和 7 都是周日 | 0 0 * * 1 command |
*/N | 每 N 个单位一次 | */5 * * * * command |
A-B/N | 范围内步进 | 0 9-18 * * 1-5 command |
A,B,C | 枚举多个值 | 0 0 * * 1,3,5 |
@reboot | 开机执行一次 | @reboot /opt/start.sh |
@daily / @hourly | 每天 / 每小时一次 | @daily /opt/backup.sh |
PATH=... | cron 默认 PATH 很精简,需显式声明 | PATH=/usr/local/bin:/usr/bin:/bin |
MAILTO="" | 关闭邮件通知 | MAILTO="" |
CRON_TZ | 指定时区 | CRON_TZ=Asia/Shanghai |
重定向 | 输出写日志而非发邮件 | command >> /var/log/job.log 2>&1 |
Common pitfalls
Symptom手动执行脚本正常,放进 crontab 却报 command not found。
Causecron 的 PATH 极精简,不含 /usr/local/bin 等交互式 shell 里配置的路径。
Fix在 crontab 顶部显式写 PATH=...;或全部使用绝对路径。
Symptomnode / python 脚本在 cron 里执行到一半失败。
Causeconda / pyenv / nvm 这类环境靠交互式 shell 的初始化脚本设置 PATH,cron 下不会加载。
Fix在脚本开头写全解释器绝对路径,或在 crontab 里直接 source 对应的初始化脚本。
Symptom任务没有任何输出,也看不到失败原因。
Causecron 把输出发邮件,而服务器往往没配 MTA,输出被直接丢弃。
Fix显式重定向:command >> /var/log/job.log 2>&1,或设 MAILTO="" 关闭通知。
Symptom日志目录被撑满,或邮件里塞满重复内容。
Cause输出长期累积没有轮转。
Fix重定向到日志文件并配 logrotate;不需要输出时用 command > /dev/null 2>&1。
Symptom以为某天会执行,实际没跑。
Cause日和周同时指定时是「或」不是「与」,且当月天数不足时会跳过。
Fix记住 0 0 13 * 5 表示 13 号或每周五;月天数不足的日子不会执行,用 L 或前一天兜底。
Symptomcron 里写的相对路径找不到文件。
Causecron 的工作目录是用户 HOME,不是你 cd 到的目录。
Fixcrontab 中用 cd /opt/app && command,或全部写绝对路径。
Symptom删除用户 crontab 后想恢复,但已无备份。
Causecrontab -u user -r 会直接删除全部条目且不提示。
Fix删除前先 crontab -u user -l > /root/crontab-user.bak 备份;改用 crontab -e 逐条编辑更安全。
Symptom改完 crontab 不生效。
Cause多数情况下保存即生效,但脚本本身没有执行权限。
Fix无需重启 cron 服务;确认脚本 chmod +x,或在命令里用 bash /path/script.sh 显式调用。
Troubleshooting
1先用最小任务验证 cron 是否工作
* * * * * echo test >> /tmp/cron-test 2>&1等一分钟看文件是否有内容;有则 cron 正常,问题在原脚本。
2检查 cron 服务是否在跑
systemctl status crond || systemctl status cronRHEL/CentOS 是 crond,Debian/Ubuntu 是 cron,名字不同。
3查看 cron 的执行日志
grep CRON /var/log/syslogDebian/Ubuntu 记在 syslog;RHEL/CentOS 改为 grep cron /var/log/cron。
4查看当前用户的 crontab
crontab -lroot 编辑他人用 crontab -u user -l / -e,注意备份后再删。
5确认解释器脚本的绝对路径
which node && which python3把输出的绝对路径写进 crontab,绕开 PATH 与环境初始化问题。
Command Examples
Debug: confirm the cron chain works at all
* * * * * echo "cron ok $(date)" >> /tmp/cron-test 2>&1不要一上来就调试正式任务。先用这条每分钟写一行并重定向 stderr——cron 最常见的故障是 PATH 太精简导致找不到命令,输出没重定向就什么都看不到。确认链路通了再换正式任务。
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>&1顶部必须显式声明 SHELL 与 PATH,否则 cron 的极简环境会让脚本找不到相对路径的命令。日志重定向到文件比用 MAILTO 发邮件更便于事后排查。
Prefer a systemd timer instead
systemctl list-timerssystemd timer 能看到下次执行时间与上次执行时间,排查「任务到底跑没跑」比翻日志直接得多;输出也能进 journalctl 而不是邮件。
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