Build a valid cron schedule by picking values. Copy the expression into your crontab, CI, or scheduler.
Use * for any, */n for every n, a,b for a list, a-b for a range.
A cron expression has five fields: minute hour day-of-month month day-of-week.
Day-of-week uses 0–6 where 0 = Sunday. Need to read an existing one? Try the
Cron Explainer.
Five-field cron is not one standard. Check the field count and the timezone before pasting, because both differ by scheduler:
| Scheduler | Fields | Timezone |
|---|---|---|
| crontab (Linux, macOS) | 5 | System timezone of the machine |
| GitHub Actions | 5 | UTC only; runs can start late when the platform is busy |
| Kubernetes CronJob | 5 | UTC unless the spec sets a timezone |
Quartz, Spring @Scheduled | 6–7 | Leading seconds field, optional trailing year |
A five-field expression pasted into a Quartz scheduler is read as seconds-first, so 0 9 * * 1-5
becomes a schedule nobody intended. Count the fields first.
Avoid the top of the hour. Almost everyone schedules at 0, so shared APIs and CI runners see a
spike every hour. Moving to 7 * * * * costs nothing and sidesteps the queue.
Assume runs will overlap. Cron starts a new run on schedule whether or not the previous one finished. If a
job can ever take longer than its interval, wrap it in a lock — flock on Linux, or a row in your
database — rather than hoping it stays fast.
Pick the hour in your users' timezone, not yours. A nightly job at 02:00 server time can land in the middle of a working day on the other side of the world.
You can't, directly. Steps are evaluated inside a single field, and the minute field resets every hour, so an
interval that doesn't divide into 60 minutes or 24 hours has no honest cron expression. Either run every 30 minutes
and let the script decide whether to act, or use a timer that measures from the last run — systemd timers
with OnUnitActiveSec do this natively.
Not in standard cron. The L character is a Quartz extension. In plain cron, run the job daily at the
target time and exit immediately unless tomorrow's date is the 1st.
Paste it into the Cron Explainer to see it in plain English, then confirm the next few fire times against what you expected. Most scheduling bugs are a misread field, not a broken scheduler.