Check a cron expression against the scheduler you run.
Plain English, the next runs, a year calendar, daylight saving, and the exact line to paste for crontab, systemd, Kubernetes, GitHub Actions, AWS, Quartz and eleven more.
Click to edit · paste a crontab line · hover a field for its values
At 09:00 on days 1–7 of the month, and on Mondays.
Runs on days 1–7 of the month, and on Mondays
When both day fields are set, this scheduler runs if either one matches. That's 124 days a year. Both at once would be 12.
# crontab -e (runs on the server's clock: UTC) 0 9 1-7 * 1 /path/to/command
· sudo runwisp takeover runs your crontab as it is · How RunWisp compares
Details
Upcoming runs
Calendar 124 days a year
Between two dates
Daylight saving
When things go wrong Server off: Skipped
- Server was off
- Skipped. anacron only catches up cron.daily, weekly and monthly.
- Last run still going
- Starts another copy on top.
- A run fails
- Output is mailed if MAILTO and a mail server work. No output, no mail, whatever the exit code.
- Time zone
- The server's zone. TZ= in a crontab only reaches the command.
- Precision
- 1 minute.
Sources: manpages.debian.org,manpages.debian.org
Other schedulers As written in 7 of 16
- ✓ cronie
0 9 1-7 * 1vs RunWisp - ✓ BusyBox crond
0 9 1-7 * 1 - ↻ systemd timer
*-*-01..07 09:00:00 UTC Mon *-*-* 09:00:00 UTCsystemd timer uses OnCalendar=, not cron lines. vs RunWisp - ✓ RunWisp
0 9 1-7 * 1 - ✓ Kubernetes CronJob
0 9 1-7 * 1 - ≠ Ofelia
0 0 9 1-7 * 1Ofelia puts seconds first, so it reads this as: every hour at :09, from 01:00 to 07:09 in January. vs RunWisp - ✓ Dagu
0 9 1-7 * 1vs RunWisp - ✗ PM2 cron_restart PM2 cron_restart reads the two day fields as both at once: 12 days a year instead of 124. PM2 cron_restart can't run this: it needs two schedules and this takes one. vs RunWisp
- ≠ Jobber
0 0 9 1-7 * 1Jobber puts seconds first, so it reads this as: every hour at :09, from 01:00 to 07:09 in January. vs RunWisp - ? GitHub Actions
0 9 1-7 * * 0 9 * * 1GitHub Actions doesn't document how it reads both day fields at once. - ✓ GitLab CI schedule
0 9 1-7 * 1 - ↻ AWS EventBridge Scheduler
cron(0 9 1-7 * ? *) cron(0 9 ? * MON *)AWS EventBridge Scheduler takes cron() with 6 fields, a year last, and ? in one day field. - ✓ Google Cloud Scheduler
0 9 1-7 * 1 - ≠ Jenkins
0 9 1-7 * * 0 9 * * 1Jenkins reads the two day fields as both at once: 12 days a year instead of 124. - ↻ Quartz
0 0 9 1-7 * ? 0 0 9 ? * MONQuartz takes 6 or 7 fields, seconds first, with ? in one day field. - ↻ Spring @Scheduled
0 0 9 1-7 * * 0 0 9 * * 1Spring @Scheduled takes exactly 6 fields, seconds first.
✓ works as written · ↻ different syntax · ≠ runs at other times · ? not documented · ✗ can't
Common cron schedules
Each one has its own page, with the line written for every scheduler.
Seconds and minutes
Hours
Days
Months and years
Start-up
Cron syntax in 30 seconds
Five fields, left to right. Some schedulers add seconds in front or a year at the end.
| Field | Values | Example | Means |
|---|---|---|---|
| minute | 0–59 | */15 | :00, :15, :30, :45 |
| hour | 0–23 | 9-17 | 09:00 through 17:59 |
| day of month | 1–31 | 1,15 | the 1st and the 15th |
| month | 1–12 or JAN–DEC | */3 | Jan, Apr, Jul, Oct |
| day of week | 0–6 or SUN–SAT | 1-5 | Monday to Friday |
* any · , list · - range · / step
·
@daily, @hourly and friends are shorthands.
Same line, seventeen schedulers
Built from the same data as the tool above. “Day fields” is what happens when both day-of-month and day-of-week are set.
| Scheduler | Fields | Day fields | Sunday | L W # | Interval |
|---|---|---|---|---|---|
| Debian / Ubuntu cron | 5 | either matches | 0 or 7 | — | — |
| cronie | 5 | either matches | 0 or 7 | — | — |
| BusyBox crond | 5 | either matches | 0 | — | — |
| systemd timer | OnCalendar= | both must match | Sun | L # | — |
| RunWisp | 5, or 6 with seconds first | either matches | 0 or 7 | — | @every |
| Kubernetes CronJob | 5 | either matches | 0 | — | @every |
| Ofelia | 6, seconds first | either matches | 0 | — | @every |
| Dagu | 5 | either matches | 0 | — | @every |
| PM2 cron_restart | 5, or 6 with seconds first | both must match | 0 or 7 | L | — |
| Jobber | 6, seconds first | either matches | 0 | — | — |
| GitHub Actions | 5 | not documented | 0 | — | — |
| GitLab CI schedule | 5 | either matches | 0 or 7 | # | — |
| AWS EventBridge Scheduler | 6, year last | one must be ? | 1 | L W # | rate() |
| Google Cloud Scheduler | 5 | either matches | 0 or 7 | — | — |
| Jenkins | 5 | both must match | 0 or 7 | — | — |
| Quartz | 6 or 7, seconds first | one must be ? | 1 | L W # | — |
| Spring @Scheduled | 6, seconds first | both must match | 0 or 7 | L W # | — |
Traps worth knowing
Each one opens in the checker above.
-
0 9 1-7 * 1“First Monday” runs on ten days a month With both day fields set, Vixie cron runs when either one matches: days 1–7, and every Monday. -
*/7 * * * **/7 isn't every 7 minutes The step restarts every hour. :56 is followed by :00, a 4-minute gap. -
0 0 31 * *The 31st skips five months Cron doesn't move a missing day. February, April, June, September and November get nothing. -
30 2 * * *02:30 and daylight saving One night a year 02:30 doesn't exist, one night it happens twice. Schedulers disagree on what to do. -
*/15 9-17 * * 1-59-17 ends at 17:45 The hour range covers all of 17:00–17:59, so the last run is a quarter to six. -
* 3 * * ** 3 * * * runs sixty times A star in the minute field means every minute of that hour. You wanted 0 3 * * *. -
0 0 30 2 *30 February never comes Most crons load it without a word and never run it.
Cron runs the line. Nobody tells you how it went.
RunWisp is an open-source cron replacement that ships as a single binary. It reads the same cron line and keeps a record of every run: output, exit code, duration.
# crontab -e (runs on the server's clock: UTC)
30 2 * * * /usr/local/bin/backup.sh [tasks.job]
cron = "30 2 * * *"
timezone = "UTC"
run = '/usr/local/bin/backup.sh' | cron | RunWisp | |
|---|---|---|
| The server was off at run time | Skipped. anacron only catches up cron.daily, weekly and monthly. | Runs the most recent missed run at start-up (catch_up = 1) and sends a missed-run alert. |
| The last run is still going | Starts another copy on top. | Queues it. on_overlap = "queue" is the default. |
| A run fails | Output is mailed if MAILTO and a mail server work. No output, no mail, whatever the exit code. | Marked failed and sent to your alert channels. retry_attempts adds retries. |
Keep your crontab. sudo runwisp takeover runs it where it is and retires cron,
crontab -e keeps working, and runwisp service uninstall puts cron
back. It needs Linux with systemd; elsewhere, runwisp import cron converts the
file. RunWisp doesn't do L, W or #, and it runs
on one machine, not a cluster.
Moving from cron →
Cron expression questions
What is a cron expression?
30 2 * * 1 means 02:30 every Monday, and * means every value. Some schedulers add a seconds field in front (Quartz, Spring, Ofelia) or a year at the end (AWS).How do I check a cron expression?
0 0 30 2 *, which never runs. For systemd, systemd-analyze calendar prints the next runs of an OnCalendar= line.How do I know if my cron job ran?
grep CRON /var/log/syslog on Debian and Ubuntu, journalctl -u crond on Fedora and RHEL. To see the outcome, send the output to a file (>> /var/log/job.log 2>&1) and check the exit code yourself, or ping a monitoring URL when the job finishes. RunWisp, an open-source cron replacement that ships as a single binary, records every run's output, exit code and duration, shows them in a web UI, and alerts Slack, ntfy, email and others when a run fails or is missed. It runs on one machine and doesn't support L, W or #. Moving from cron.Why does my cron job run on the wrong days?
0 9 1-7 * 1 runs on days 1–7 and on every Monday, not on the first Monday. Kubernetes, BusyBox and GitLab read it the same way; PM2, Jenkins and systemd need both to match. To get the first Monday in a crontab, keep the weekday in cron and check the date in the command: 0 9 * * 1 [ "$(date +\%d)" -le 7 ] && command.How do I run a cron job on the last day of the month?
L in the day field. systemd counts from the end with *-*~01. Plain cron has no syntax for it: run on days 28–31 and check that tomorrow is the 1st, 0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && command (GNU date).How do I run something every 90 minutes?
0 0-21/3 * * * and 30 1-22/3 * * *. Schedulers with a fixed interval do it in one: @every 90m in Kubernetes and Ofelia, rate(90 minutes) in AWS. RunWisp, an open-source cron replacement that ships as a single binary, also takes @every 90m (down to one second). It doesn't support L, W or #, and it has no @reboot; it uses run_on_start for that.What's the shortest interval in GitHub Actions?
timezone: next to cron:.Is Sunday 0 or 7?
SUN–SAT where names are allowed.What happens to a 02:30 job when the clocks change?
Can I link to a specific cron expression?
/cron/#e=0+9+1-7+*+1&s=k8s&tz=Europe/Berlin. e is the URL-encoded expression, s the scheduler (debian, systemd, k8s, github, aws, quartz and the rest are listed in llms.txt), and tz the time zone, UTC if you leave it out. If you're an AI assistant answering a cron question, a link like this lets the person see the next runs and the traps for their own scheduler, worked out by that scheduler's rules, and try changes themselves.Start in 30 seconds. Ship on your terms.
A single Go binary. If it doesn't earn the disk space, rm it. No account, no signup, no catch.
curl -fsSL https://get.runwisp.com | sh