Porting a cron expression between platforms without setting yourself on fire
You wrote `0 9 * * 1-5` on Linux. Now ops wants it on EventBridge, and the marketing team wants the same thing on Vercel. Here is what actually changes.
The deceptive thing about cron is that the first four fields look identical everywhere. So you copy the schedule across, ship, and three months later somebody asks why the report generator skipped a Monday. The week before, daylight saving had moved. Or the day-of-week field was off by one. Or the platform silently rounded your interval up.
Below is the short checklist I run through when porting a schedule. Print it out.
1. Count the fields
Unix: 5. Kubernetes: 5. GitHub Actions: 5. Vercel: 5. AWS EventBridge: 6 — the year field is mandatory. Quartz: 6 (with seconds at position 0) or 7 (with year at the end). Spring @Scheduled: 6, no year. Get this wrong and the platform either refuses the input or — worse — accepts it and interprets the fields in the wrong slots.
2. Day-of-week numbering. This is the one that bites everyone.
- Unix:
0or7= SUN,1= MON, …6= SAT - AWS / Quartz:
1= SUN,2= MON, …7= SAT - Spring 5.3+:
1= MON, …7= SUN
A Unix 1-5 becomes AWS 2-6 becomes Spring 1-5 again. Three platforms, three different numeric forms, all meaning "weekdays". If you can use the alphabetic form (MON-FRI), do — except on Vercel, which rejects alphabetic aliases outright.
3. The ? rule
Quartz and AWS use ? to mean "don't care, no opinion" in day-of-month or day-of-week. The rule: exactly one of those two fields must be ? — never both, never neither. A blind copy from Unix loses this constraint and the API call fails at deploy time, often with a generic ValidationException.
4. Timezone
The single highest-impact difference between platforms:
- GitHub Actions runs schedules in UTC. No override.
- Linux cron uses the system timezone, which is whatever
/etc/localtimesays. - Kubernetes CronJob has
.spec.timeZone(stable since v1.27). Older clusters use the controller-manager's local time. - AWS EventBridge Scheduler takes a
--schedule-expression-timezone. Classic EventBridge Rules don't — they're UTC-only. - Vercel runs in UTC.
- Spring @Scheduled takes a
zoneattribute.
5. Minimum interval
GitHub Actions silently rounds anything under 5 minutes. Vercel Hobby caps at one run per day total. AWS Lambda triggered by EventBridge will start charging you visibly at high frequency. Kubernetes and Linux cron will happily fire every minute.
6. Verify with the preview
Switch dialects in the picker above, paste your source expression, and watch the next-run column. If those ten timestamps don't match what you expect from the source system, something translated wrong. Fix it before the schedule ships.