I've been working with cron expressions across different platforms for a while now, and I keep running into the same traps — some of them cost me hours of debugging, one of them cost me a full day of missed backups.
Here are 7 gotchas I've collected, with fixes for each.
1. "Every 6 hours" vs "every minute of hour 6"
This is the classic. You write:
0 6 * * *
Thinking it means "every 6 hours." It doesn't. It means "at 06:00 every day." What you actually want:
0 */6 * * *
The */6 in the hour field means "every 6th hour." Without the slash, you're just picking hour 6.
2. GitHub Actions is UTC-only and lies about timing
Your workflow says:
on:
schedule:
- cron: '0 9 * * 1-5'
You think it runs at 9am your time. It runs at 9am UTC. If you're in US Eastern, that's 5am. If you're in Beijing, that's 5pm.
Worse: GitHub Actions can delay scheduled runs by 5-30 minutes under heavy load. A job scheduled for 9:00 UTC might actually fire at 9:22. This matters if you have downstream dependencies.
3. */5 doesn't work on AWS EventBridge
You write:
cron(*/5 * * * ? *)
EventBridge rejects it. AWS uses 0/5 instead of */5:
cron(0/5 * * * ? *)
No error message tells you this clearly. You just get a generic validation failure.
4. Day-of-month + day-of-week: OR vs AND
This one is genuinely confusing. The expression:
0 8 15 * 1
On Linux cron: fires on the 15th of every month OR every Monday. (OR semantics)
On AWS EventBridge / Quartz: you can't even set both — one field must be ?.
If you want "the first Monday of every month" on Linux, you need a shell guard:
0 8 1-7 * 1 [ $(date +\%u) -eq 1 ] && /your/command
5. Vercel Hobby plan: 1 cron job per day
You deploy a Vercel cron that runs every hour:
{
"crons": [{
"path": "/api/cleanup",
"schedule": "0 * * * *"
}]
}
On the Hobby plan, this silently gets throttled to once per day. No warning in the build output. You find out when your cleanup hasn't run for 23 hours.
Pro plan minimum interval is 1 minute. Hobby is 1 per day. Period.
6. Kubernetes timeZone field needs v1.27+
You add this to your CronJob spec:
spec:
timeZone: "America/New_York"
schedule: "0 9 * * *"
On clusters older than 1.27, the timeZone field is silently ignored. Your job runs in UTC. No error, no warning.
Always check your cluster version before relying on this field.
7. Quartz uses different weekday numbers
Linux cron: 0 or 7 = Sunday, 1 = Monday
Quartz: 1 = Sunday, 2 = Monday
So 0 8 ? * 2 means Monday on Quartz but Tuesday on Linux. Copy-paste between platforms and you've shifted your entire schedule by a day.
Why I built a tool for this
After hitting most of these myself, I built cronwiz.dev — type a schedule in plain English, get the cron expression for 7 platforms (Linux, AWS, K8s, GitHub Actions, Vercel, Quartz, Cloudflare Workers) with platform-specific warnings baked in.
It also works in reverse: paste a cron expression and get a human-readable explanation. Useful when you're debugging someone else's crontab at 3am.
Free, no signup. The source is on GitHub.
I'm also building a cron monitor (dead-man's-switch style — your job pings a URL on success, you get alerted if it doesn't fire on time). Waitlist is on the site if that's interesting.
What cron gotchas have bitten you? I'm always looking for more edge cases to add to the tool.
Top comments (0)