Crontab 速查表:50+ Cron 表达式、语法详解与现代调度器完全指南
cron 表达式由五个字段加一条命令组成:分、时、日、月、星期。这套语法从 1979 年起驱动 Unix 任务调度,现在也驱动 Kubernetes CronJob、GitHub Actions、AWS EventBridge 和 Vercel cron 触发器。学一次就够用了。
这篇文章面向当下就要写表达式的开发者:一个 Linux 定时任务,一个 Kubernetes CronJob,一个 GitHub Actions 触发器,或者排查为什么本该每五分钟跑一次的任务只在整点触发。直接看下面的快速参考表复制表达式;想看字段规则就跳到「语法详解」;或者打开 Crontab 生成器 — Cron 表达式构建与解析 —— 一个在浏览器中运行的隐私优先的 crontab guru 替代工具 —— 实时验证。
Cron 表达式快速参考表
下面三十个表达式覆盖了约 90% 的真实调度需求。每一条都是合法的 POSIX 五字段 cron,可直接粘贴到 crontab -e、Kubernetes 的 schedule: 或 GitHub Actions 的 cron: 中。
| 调度场景 | Cron 表达式 | 中文解释 |
|---|---|---|
| 每分钟 | * * * * * | 全天每一分钟 |
| 每 5 分钟 | */5 * * * * | 第 0、5、10、…、55 分钟 |
| 每 15 分钟 | */15 * * * * | 第 0、15、30、45 分钟 |
| 每 30 分钟 | */30 * * * * | 第 0 和第 30 分钟 |
| 每小时 | 0 * * * * | 每个整点 |
| 每 2 小时 | 0 */2 * * * | 第 0、2、4、…、22 时 |
| 每 6 小时 | 0 */6 * * * | 第 0、6、12、18 时 |
| 每天两次(早 9 + 晚 9) | 0 9,21 * * * | 9 点和 21 点整 |
| 工作日每天上午 9 点 | 0 9 * * 1-5 | 周一至周五 09:00 |
| 周末每天上午 9 点 | 0 9 * * 0,6 | 周六周日 09:00 |
| 每天午夜 | 0 0 * * * | 每日 00:00 |
| 每天凌晨 2:30 | 30 2 * * * | 低峰批处理窗口 |
| 每周一上午 9 点 | 0 9 * * 1 | 周一 09:00 |
| 每周五下午 5 点 | 0 17 * * 5 | 周五 17:00 |
| 每周日午夜 | 0 0 * * 0 | 等价于 @weekly |
| 每月 1 号午夜 | 0 0 1 * * | 每月 1 日 00:00,等价于 @monthly |
| 每月 15 号中午 | 0 12 15 * * | 月中发薪窗口 |
| 每月最后一天(需 wrapper) | 0 0 28-31 * * + 脚本 | 需要日期判断 |
| 每季度(1/4/7/10 月 1 日) | 0 0 1 JAN,APR,JUL,OCT * | 每季度第一天 |
| 每年(1 月 1 日) | 0 0 1 1 * 或 @yearly | 元旦零点 |
| 工作日 9-17 点每 5 分钟 | */5 9-17 * * 1-5 | 工作时间轮询 |
| 周末每 30 分钟 | */30 * * * 0,6 | 周六周日监控 |
| 每小时两次,15 和 45 分 | 15,45 * * * * | 错开整点高峰 |
| 每月第一个周一(需 wrapper) | 0 9 1-7 * 1 + AND 判断 | 需 wrapper(见下文) |
| 宏 | @hourly @daily @weekly @monthly @yearly | 非标准,但被广泛支持 |
| 仅在重启时 | @reboot | 非标准,仅 vixie cron 支持 |
把上面任意一条粘贴到 Crontab 生成器 — Cron 表达式构建与解析,可以预览接下来五次触发时间,是上线前最快的烟雾测试。
Cron 语法详解:五个字段
cron 表达式是由空格分隔的五个字段,再加一条命令。每个字段控制调度的一个维度。这套语法是本文涉及到的所有调度器的共同基础。
┌──────────── minute (0 - 59)
│ ┌────────── hour (0 - 23)
│ │ ┌──────── day-of-month (1 - 31)
│ │ │ ┌────── month (1 - 12 or JAN-DEC)
│ │ │ │ ┌──── day-of-week (0 - 6 or SUN-SAT; 0 and 7 both mean Sunday)
│ │ │ │ │
* * * * * command-to-run
记忆口诀:「分时日月周」,从左到右,由小到大。
各字段允许的取值
| 字段 | 范围 | 别名 | 备注 |
|---|---|---|---|
| 分 | 0-59 | 无 | 0 表示「整点」 |
| 时 | 0-23 | 无 | 24 小时制;0 是午夜,12 是正午 |
| 日 | 1-31 | 无 | 当月不存在的日期会静默地永不触发(例如 2 月 31 日) |
| 月 | 1-12 | JAN、FEB、MAR、…、DEC | 大小写不敏感 |
| 周 | 0-7 | SUN、MON、TUE、…、SAT | 0 和 7 都表示周日 |
运算符详解
五个运算符就能覆盖所有标准 cron 表达式:
| 运算符 | 含义 | 示例 | 展开结果 |
|---|---|---|---|
* | 任意值 | * * * * * | 每分钟 |
, | 列表 | 0 9,12,17 * * * | 09:00、12:00、17:00 |
- | 闭区间范围 | 0 9-17 * * * | 09:00 到 17:00 每个整点 |
/ | 步长 | */15 * * * * | 第 0、15、30、45 分钟 |
| 混合 | 组合 | 0 9-12,14-17 * * * | 上午+下午,跳过午饭时间 |
步长运算符要小心。*/N 是相对于字段的最小值对齐的,不是相对于当前时间。*/15 的含义是「每个小时的第 0、15、30、45 分钟」,不是「从现在起每 15 分钟一次」。12:03 保存表达式,下一次触发就是 12:15。如果基数不是通配符,5/15 读作「从 5 开始,每 15 分钟一次」:第 5、20、35、50 分钟。
月份与星期使用名称
月份和星期可以写成名称,大小写不敏感:
0 0 1 JAN,APR,JUL,OCT * # 每季度的第一天
0 9 * * MON-FRI # 工作日上午 9 点
0 17 * * FRI # 周五下午 5 点
代码评审时名称更易读,数字形式可移植性稍好一些。一个项目内统一风格即可。
非标准宏:@reboot、@daily 及其家族
大多数 cron 实现都接受六个快捷宏:
| 宏 | 展开为 | 含义 |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | 每年一次,1 月 1 日午夜 |
@monthly | 0 0 1 * * | 每月 1 日午夜 |
@weekly | 0 0 * * 0 | 每周日午夜 |
@daily / @midnight | 0 0 * * * | 每天午夜 |
@hourly | 0 * * * * | 每个整点 |
@reboot | (特殊) | cron 守护进程启动时执行一次 |
这些宏是非标准的:vixie cron 和 cronie 支持,但 Kubernetes CronJob、GitHub Actions、AWS EventBridge 都不认。要写可移植的表达式,就写五字段形式。@reboot 在容器里几乎用不上,因为 cron 守护进程通常不是 init 进程。
50+ 可复制 Cron 表达式(按用途分组)
这一节按六个用途分桶,给出更密集的 cron 例子。
每 N 分钟
* * * * * # 每分钟
*/2 * * * * # 每 2 分钟
*/5 * * * * # 每 5 分钟 —— 经典的「每 5 分钟」场景
*/10 * * * * # 每 10 分钟
*/15 * * * * # 每 15 分钟
*/30 * * * * # 每 30 分钟
0,30 * * * * # 明确指定第 0 和第 30 分钟(与 */30 等价)
*/45 * * * * # 警告:只在第 0 和第 45 分钟触发,然后回绕
*/45 是一个常见的陷阱:分钟范围是 0-59,所以会落在第 0 和第 45 分钟,下一个小时再次回绕到第 0 分钟。要真正实现 45 分钟的等间隔节奏,需要外部 worker。
整点变体
0 * * * * # 每小时 :00
30 * * * * # 每小时 :30
0 */2 * * * # 每 2 小时,偶数时刻
0 */6 * * * # 每 6 小时
0 */12 * * * # 每天两次,00:00 和 12:00
15 */2 * * * # 每 2 小时,错开 15 分钟(避开整点高峰)
每天的固定时间
0 0 * * * # 午夜(= @daily / @midnight)
30 2 * * * # 02:30 —— 低峰批处理窗口
0 9 * * * # 09:00
45 23 * * * # 23:45 —— 日终汇总
0 9,12,17 * * * # 一天三次
0 9-17 * * * # 从 09:00 到 17:00 每个整点
每周调度
0 9 * * 1-5 # 工作日上午 9 点
0 9 * * 0,6 # 周末上午 9 点
0 18 * * 5 # 周五下午 6 点
0 0 * * 0 # 周日午夜(= @weekly)
0 9 * * MON,WED,FRI # 周一/三/五上午 9 点
*/30 9-17 * * 1-5 # 工作日工作时间每 30 分钟一次
每月与每季度
0 0 1 * * # 每月 1 日午夜(= @monthly)
0 0 15 * * # 每月 15 日 —— 发薪窗口
0 0 1,15 * * # 1 日和 15 日 —— 半月节奏
0 0 1 */3 * # 每季度:1 月、4 月、7 月、10 月的 1 日
0 0 1 JAN,APR,JUL,OCT * # 同上,月份用名称
0 0 28-31 * * # 月末几天 —— 搭配日期判断 wrapper
「每月最后一天」在 POSIX cron 中没有原生表达式。可以写一个 wrapper 判断 date -d tomorrow +%d = 01,或者改用原生支持该语法的调度器(Quartz 有 L,Kubernetes 没有)。
每年与宏快捷写法
0 0 1 1 * # 1 月 1 日午夜(= @yearly / @annually)
0 0 25 12 * # 圣诞节午夜
@yearly # = 0 0 1 1 *
@monthly # = 0 0 1 * *
@weekly # = 0 0 * * 0
@daily # = 0 0 * * *
@hourly # = 0 * * * *
@reboot # 特殊:守护进程启动时执行一次(仅 vixie cron)
把上面任何一条粘贴到 Crontab 生成器 — Cron 表达式构建与解析都可以查看接下来五次触发时间,是上线前最便宜的烟雾测试。
Cron vs systemd timer vs 云调度器:决策矩阵
cron 是默认选项,但不一定是最优解。下面对七种最常见的调度器做了对比。无论你在判断 cron 还是 systemd timer,对比 Kubernetes CronJob 与 Vercel cron,还是从 crontab 迁移到托管云服务,都用得上。
| 特性 | vixie cron | systemd timer | K8s CronJob | GHA schedule | AWS EventBridge | Vercel Cron | Cloudflare Workers |
|---|---|---|---|---|---|---|---|
| 字段语法 | 5 字段 POSIX | OnCalendar 规范 | 5 字段 POSIX + timeZone | 5 字段 POSIX | 6 字段 Quartz,含 ? | 5 字段 POSIX | 5 字段 POSIX |
| 最小间隔 | 1 分钟 | 1 秒 | 1 分钟 | 尽力而为,建议 ≥15 分钟 | 1 分钟 | 1 分钟(Pro 计划) | 1 分钟 |
| 显式时区 | CRON_TZ= | Persistent=true | spec.timeZone(1.27+) | 仅 UTC | ScheduleExpressionTimezone | 仅 UTC | 仅 UTC |
| 漏跑补偿 | 否(用 anacron) | 是(Persistent=true) | 是(startingDeadlineSeconds) | 否 | 是 | 否 | 否 |
| 重试 / 退避 | 否 | 部分 | 是(backoffLimit) | 失败时重试 | 是 | 否 | 是 |
| 并发控制 | 否(用 flock) | 部分 | 是(concurrencyPolicy) | 否 | 否 | 否 | 否 |
@reboot 支持 | 是 | 是(通过 OnBootSec=) | 否 | 否 | 否 | 否 | 否 |
systemd timer:什么时候比 cron 更合适
在基于 systemd 的 Linux 上,timer 是一个实用的替代方案:日历式语法可读性更好,原生接入 journal,还能补漏跑。一个 timer 和对应的 service:
# daily-report.timer
[Unit]
Description=Run daily report at 9 AM
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
Unit=daily-report.service
[Install]
WantedBy=timers.target
# daily-report.service
[Unit]
Description=Daily report job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-report.sh
User=reporter
用 systemctl enable --now daily-report.timer 启用。最大的差异在 Persistent=true:如果 9 点机器是关机的,开机后 timer 会立即补跑。vixie cron 没有等价能力,除非额外用 anacron。关于服务侧的安全加固,参考安全最佳实践。
Kubernetes CronJob
Kubernetes 在 POSIX 调度的基础上加了一层原语,用于控制并发、保留历史和指定时区:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York" # Kubernetes 1.27+
concurrencyPolicy: Forbid # never run two at once
startingDeadlineSeconds: 300 # skip if delayed >5 min
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: reporter:1.4.0
command: ["/usr/local/bin/report.sh"]
concurrencyPolicy: Forbid 就是你在 Kubernetes 里的 flock 等价物。不加这一项,一个跑得久的任务会和下一次触发叠在一起。所有可配项见下文「Kubernetes CronJob 字段参考」。
GitHub Actions schedule 的注意事项
GitHub Actions 接受标准的五字段 POSIX cron:
on:
schedule:
- cron: '0 9 * * 1-5' # weekdays at 9 AM UTC
但这是「尽力而为」:在 GitHub runner 负载高时,任务可能延迟几分钟,甚至整次跳过。不要使用短于 15 分钟的间隔。GHA 没有时区设置,永远是 UTC。
AWS EventBridge:Quartz 风格的六字段
AWS EventBridge 使用 Quartz 变体的 cron,有六个字段,并且要求两个日字段之一必须是 ?:
cron(0 9 * * ? *)
字段顺序是:Minutes Hours Day-of-month Month Day-of-week Year。当其中一个日字段有约束时,另一个必须是 ?(Quartz 用这个语法来消解 POSIX 的 OR 歧义)。从 Linux crontab 直接复制过来肯定通不过校验。
Vercel Cron、Cloudflare Workers、Render Cron Jobs
新兴的 serverless 平台统一选用了五字段 POSIX。Vercel cron job 写在 vercel.json 里:{ "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] }。Cloudflare Workers 的 Cron Triggers 写在 wrangler.toml 里:
[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]
Render 用的是 render.yaml。这三家都按 UTC 跑,并且不能为单条调度单独覆盖时区,从一开始就按 UTC 设计。
7 个 Cron 调试陷阱(以及怎么抓到它们)
绝大多数「我的 cron 没跑」的报告,根因都在下面这七个之一。怀疑调度器之前,先把这张清单走一遍。
陷阱 1:PATH 极度精简
cron 启动任务时给的是最精简的 $PATH,通常只有 /usr/bin:/bin。你交互式 shell 里有 /usr/local/bin、~/.cargo/bin,还有十几条来自 .bashrc 的补充。这些 cron 一概不知道。环境变量类问题里,这一条出现得最频繁。
症状:node: command not found。修法:在 crontab 顶部显式设置 PATH,或使用绝对路径。
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
*/15 * * * * /usr/local/bin/poll-api.sh
0 9 * * * /home/deploy/.cargo/bin/my-rust-cli
陷阱 2:stdout 和 stderr 被静默丢弃
默认情况下,cron 的输出送到一个没人看的邮件 spool 里。任务就这么静默失败了。把两路流都重定向:
*/15 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
JSON 输出可以管道送 jq处理;提取日志行参考正则表达式速查表。systemd timer 用 journalctl -u your-timer.service 就能看到所有输出。
陷阱 3:开发与生产时区错位
你在纽约的笔记本上写 0 9 * * *,以为是美东时间早 9 点。生产服务器跑在 UTC。cron 按 UTC 早 9 点触发,也就是美东凌晨 4 点,没人发现。修法:服务器统一用 UTC、调度按 UTC 写,或者显式锁定时区。
CRON_TZ=America/New_York
0 9 * * * /usr/local/bin/morning-report.sh
CRON_TZ 从 vixie cron 3.0 起支持;Kubernetes 1.27+ 提供 spec.timeZone;AWS EventBridge 用 ScheduleExpressionTimezone;GitHub Actions 永远是 UTC。UTC、夏令时和 epoch 换算细节参考 Unix 时间戳完全指南。
陷阱 4:命令中未转义的 %
cron 会把未转义的 % 当作换行符,后面的内容会被当成标准输入喂给命令。所以 date +"%Y-%m-%d" 直接挂掉。要把每个 % 转义成 \%,或者干脆把逻辑搬进脚本:
0 0 * * * echo "Run at $(date +"\%Y-\%m-\%d")" >> /tmp/log
陷阱 5:执行时间重叠
一个 */5 * * * * 的任务如果偶尔跑七分钟,下一次实例就会在上一次还没结束时被启动。两个进程会在同一行数据、同一个锁文件、同一个 API 配额上互殴。用 flock 串行化:
*/5 * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh
-n 表示锁被占用时立刻退出。在 Kubernetes 里则设置 concurrencyPolicy: Forbid。锁文件的权限也得注意,参见安全最佳实践。
陷阱 6:容器里的 @reboot
@reboot 在 cron 守护进程启动时跑一次。在 VM 里它对应开机时刻;在容器里 cron 守护进程通常不是 PID 1,甚至根本没在跑。容器里不要用 @reboot,把启动时跑一次的逻辑放在 entrypoint 或 init container 里。
陷阱 7:POSIX 中日与星期的 OR 语义
这是代价最高的 cron 陷阱。POSIX 规则:当日字段和星期字段都被约束(两者都不是 *)时,两者中任意一个匹配就会触发。
0 0 1 * 5 看起来像「每月 1 号的午夜,且必须是周五」,但实际上它在每月 1 号以及每个周五都会触发,每月多出六到十次。
# 错:看起来像「月初 1 号,且必须是周五才跑」
0 0 1 * 5
# 对:只保留一个约束
0 0 1 * * # 每月 1 号
0 0 * * 5 # 每个周五
# AND 语义需要 wrapper
0 0 1-7 * 5 [ "$(date +\%u)" = "5" ] && /script # 仅当月第一个周五
把可疑表达式粘进 Crontab 生成器 — Cron 表达式构建与解析,看接下来几次触发时间,OR 陷阱一目了然。
现代调度器:什么时候不该用 cron
cron 适合「在大致这个时间、按固定节奏跑一条命令」。下面这些相邻问题它就不擅长了:
- 有依赖关系的工作流(先 A,A 成功后跑 B):用 Airflow、Prefect、Dagster。
- 重试、指数退避、死信队列:用 Temporal、AWS Step Functions、Sidekiq。
- 亚分钟级间隔:用长生命周期 worker,在迭代间 sleep。
- 秒级精度:用专用守护进程;托管调度器都会声明不保证精确触发。
- 事件驱动型任务:用 webhook、消息队列、CDC 流。
cron 并没有被淘汰:Airflow、Step Functions、Sidekiq 在工作流的入口都接受 cron 表达式。这套五字段语法是可复用的。
Kubernetes CronJob 字段参考
下面是 Kubernetes CronJob 语法的完整字段参考:
| 字段 | 默认值 | 作用 |
|---|---|---|
schedule | 必填 | POSIX 5 字段 cron 表达式 |
timeZone | 控制器所在时区 | 显式时区(1.27+);用 IANA 名称 |
concurrencyPolicy | Allow | Forbid 在上次还在跑时跳过;Replace 取消上次 |
startingDeadlineSeconds | 无限制 | 延迟超过该秒数则跳过 |
successfulJobsHistoryLimit | 3 | 保留多少个成功的 Job |
failedJobsHistoryLimit | 1 | 保留多少个失败的 Job |
suspend | false | 暂停但不删除 |
backoffLimit | 6 | 标记 Job 失败前的 Pod 重试次数 |
activeDeadlineSeconds | 未设置 | Pod 运行时长的硬上限 |
ttlSecondsAfterFinished | 未设置 | Job 结束后多少秒自动删除 |
两个常见坑:忘掉 timeZone 会让调度跟着 kube-controller-manager 所在主机的时区跑(在托管 Kubernetes 上完全不可预测);如果是每分钟一次的调度,默认的 successfulJobsHistoryLimit: 3 会让 Job 对象按每分钟三个的速度堆积,除非配合 ttlSecondsAfterFinished 一起设置。
跨平台的 Cron 等价物
macOS launchd。Apple 推荐用 launchd 替代 cron。一个 launchd 任务是放在 ~/Library/LaunchAgents/ 下的 .plist:
<plist version="1.0"><dict>
<key>Label</key><string>com.example.daily</string>
<key>ProgramArguments</key><array><string>/usr/local/bin/daily.sh</string></array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>
用 launchctl load ~/Library/LaunchAgents/com.example.daily.plist 加载。和 cron 不同,launchd 会在睡眠/唤醒后补上漏掉的执行。
Windows 任务计划程序 用 schtasks:
schtasks /create /tn "DailyReport" /tr "C:\scripts\report.bat" /sc DAILY /st 09:00
schtasks /create /tn "EveryFifteen" /tr "C:\scripts\poll.bat" /sc MINUTE /mo 15
在 WSL 上原生 Linux cron 也能用,但会话退出就停了。要让 WSL 任务始终在线,用任务计划程序拉起。
Docker 容器里的 cron。大多数精简镜像(alpine、debian-slim、distroless)出厂不带 cron 守护进程。装一个 cronie 或 busybox-cron,再用 tini 或 s6-overlay 把它跑成 PID 1。不过通常更好的选择是直接用 Kubernetes CronJob。
进阶技巧与模式
每月最后一天
cron 没有原生的「最后一天」运算符。在 28-31 这几天都跑一次,再判断明天是不是 1 号:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh
当月第 N 个星期几
「第一个周一」用同样的 wrapper 模式,把日字段限制在 1-7,再判断星期:
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh
「最后一个周五」就把日改成 25-31 再加星期判断。
用随机偏移分散负载
当大量机器跑同一个 cron 时,0 0 * * * 会在 UTC 午夜造成 thundering herd。撒点随机延迟:
RANDOM_DELAY=10 # cronie / anacron,单位分钟
0 0 * * * /usr/local/bin/job.sh
0 0 * * * sleep $((RANDOM \% 600)); /usr/local/bin/job.sh # 可移植写法
心跳监控
cron 是静默失败的。dead-man’s-switch 模式可以解决:任务每次成功后向监控服务发一次 ping;如果预期的 ping 没到,监控服务报警。Healthchecks.io、Cronitor、Dead Man’s Snitch 都有免费层。
*/15 * * * * /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid
如果监控逻辑要根据响应码分支(200 健康、429 限流、503 降级),参考 HTTP 状态码速查表。
幂等性是任务自身的属性,不是调度器的
cron 没有重试、没有漏跑补偿、没有并发控制。最可靠的修法是让任务本身可以安全地重复执行。把「上午 9 点发当日报表」重新设计为「如果今天还没发过,就发」,漏跑、重复、人工补跑最终都会收敛到同一个状态。
常见问题
*/5 * * * * 真的是每 5 分钟一次吗?
几乎是 —— */5 * * * * 对齐到第 0 分钟,而不是「从现在起每 5 分钟」。每小时在第 0、5、10、…、55 分钟触发。步长 */N 是相对于字段的最小值,而不是当前时间。12:03 保存表达式,下一次是 12:05,不是 12:08。
0 0 * * * 在 cron 里是什么意思?
0 0 * * * 表示每天午夜(00:00)按服务器本地时区执行。字段含义:分 0、时 0、任意日、任意月、任意星期。等价于宏 @daily 或 @midnight。要锁定时区,在 crontab 顶部加 CRON_TZ=America/New_York。
怎么让 cron 任务每 30 秒跑一次?
标准 POSIX cron 做不到,最小粒度是 1 分钟。三个 workaround:两个 * * * * * 任务错开排,其中一个加 sleep 30 &&;或者用 systemd timer 配 OnCalendar=*:*:0/30;或者写一个长驻 worker 在迭代间 sleep。最后这种通常是正解。
cron 默认用什么时区?
服务器的系统本地时区(/etc/timezone 或 TZ 环境变量)。一个写在 UTC 服务器上的 9 点 cron 会按美东凌晨 4 点跑。修法:在 crontab 顶部设置 CRON_TZ=,或者服务器统一 UTC、调度按 UTC 设计。GitHub Actions 永远是 UTC;Kubernetes 1.27+ 支持 spec.timeZone。
为什么我的 cron 任务没运行?
如果 cron 任务没有运行,按顺序检查:cron 守护进程是否在跑(systemctl status cron);crontab 里是否设了 $PATH;stderr 有没有捕获(>> log 2>&1);用户的 crontab 是否真的加载了(crontab -l);命令里的 % 有没有转义;时区是否符合预期。绝大多数「没运行」的报告都卡在第二或第三条。
Kubernetes CronJob 的语法和 Linux cron 一样吗?
调度字段是一样的,两者都用 POSIX 五字段 cron。Kubernetes 额外加了 spec.timeZone(1.27+)、用于并发控制的 concurrencyPolicy、用于漏跑补偿的 startingDeadlineSeconds,以及用于暂停的 suspend: true。Linux cron 没有这些,要靠 flock 和 anacron 凑合。
@reboot 和 @daily 有什么区别?
@daily 是 0 0 * * * 的宏,每天午夜按固定节奏跑。@reboot 是在 cron 守护进程启动时跑一次,没有任何周期性。@reboot 在 vixie cron 和 cronie 里支持,但在 Kubernetes CronJob、GitHub Actions 和 AWS EventBridge 里都不支持。在容器里,@reboot 也几乎不会触发。
cron 和 crontab 有什么区别?
cron 是运行计划任务的后台守护进程,crontab 是列出这些任务的文件(也是编辑该文件的 crontab 命令)。守护进程按计划读取每个用户的 crontab,运行执行时间与 cron 表达式匹配的命令。所以 cron 是引擎,crontab 是配方。