Cron twice a day

0 9,21 * * *

0 9,21 * * * runs a job twice a day, at 09:00 and 21:00. A comma makes a list, so the hour field 9,21 picks exactly those two hours, and the 0 in the minute field runs each one on the hour.

When it fires

Checked at 09:58 UTC on October 14, that morning’s run has passed, so the next five are:

Oct 14 21:00  Oct 15 09:00  Oct 15 21:00  Oct 16 09:00  Oct 16 21:00

Any two hours work the same way: 0 0,12 * * * is midnight and noon, the same schedule as 0 */12 * * *, and 0 8,17 * * 1-5 is the start and end of a working day, Monday to Friday.

A comma, not a dash

0 9-21 * * * is a range: it runs every hour from 09:00 to 21:00, 13 times a day. One character turns two runs into thirteen, so read the hour field twice before saving.

Two different minutes

The fields combine as a grid: every listed minute in every listed hour. So 0,30 9,21 * * * doesn’t mean 09:00 and 21:30. It runs four times, at 09:00, 09:30, 21:00 and 21:30. For two times with different minutes, write two lines:

0 9 * * *     /usr/local/bin/digest.sh
30 21 * * *   /usr/local/bin/digest.sh

If the two runs do different work, such as a morning summary and an evening cleanup, two lines with two commands are clearer anyway.

The same schedule elsewhere

  • crontab: 0 9,21 * * * /usr/local/bin/digest.sh, in the server’s timezone unless the crontab sets CRON_TZ.
  • GitHub Actions: cron: '0 9,21 * * *', in UTC. For different minutes, list two - cron: entries under on.schedule.
  • Kubernetes CronJob: schedule: "0 9,21 * * *". A CronJob takes one schedule, so two different minutes need two CronJobs.
  • AWS EventBridge: cron(0 9,21 * * ? *).
  • systemd timer: OnCalendar=09,21:00, which systemd-analyze calendar normalizes to *-*-* 09,21:00:00. A timer takes several OnCalendar= lines, so 09:00 and 21:30 fit in one.
  • Spring @Scheduled: 0 0 9,21 * * *.

Try it in the Cron Builder

At 09:00 and 21:00

Read as a crontab line, where Sunday is 0 or 7.

minutehourday of monthmonthday of week
09,21***

Next runs

Working out the next runs from your clock…

Runs in your browser. Nothing you enter is sent anywhere.