Skip to content
Webmaster Tools
Dev utilitiesCron Parser1st and 15th of the month

Cron on the 1st and 15th of the month

Run a cron job twice a month, on the 1st and the 15th, with 0 0 1,15 * *. Payroll-style schedules, the 15th and last day, the weekday trap, and the AWS and GitHub forms.

Cron expression
0 0 1,15 * *
See the next run times

The expression

0 0 1,15 * *

Runs at midnight on the 1st and the 15th of every month: 24 times a year, the classic semi-monthly schedule for payroll, invoicing and reports.

Field Value Meaning
Minute 0 at minute 0
Hour 0 at midnight
Day of month 1,15 the 1st and the 15th
Month * every month
Day of week * any day of the week

A comma makes a list: each value is a separate match, so 1,15 is the 1st or the 15th. Every month has both days, so neither run is ever skipped.

Variants

Schedule Expression
1st and 15th at 9am 0 9 1,15 * *
1st and 16th (two equal halves) 0 0 1,16 * *
15th and last day of the month 0 0 15,28-31 * * plus a test on the 28-31 runs
The 1st only 0 0 1 * *
The last day only 0 0 28-31 * * plus a test
Every other week instead workaround

For "the 15th and the last day", which is how many payrolls actually work, the 28-31 runs need the tomorrow-is-the-1st check:

0 0 15,28-31 * * [ "$(date +\%d)" = 15 ] || [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/payroll.sh

% must be escaped as \% in a crontab. On AWS EventBridge, use two rules instead: cron(0 0 15 * ? *) and cron(0 0 L * ? *).

Twice a month is not every two weeks

The 1st-and-15th schedule has gaps of 14 days (1st to 15th) and 13 to 16 days (15th to the next 1st, depending on the month). That's what semi-monthly means. If the job must run exactly every 14 days, on the same weekday, you want every other week, which cron needs a workaround for.

Where to write it

  • crontab (Linux, macOS): 0 0 1,15 * * command in crontab -e, in the server's time zone.
  • GitHub Actions: cron: '0 0 1,15 * *', in UTC; the run lands on the evening before for anyone west of UTC, so consider 0 12 1,15 * *.
  • Kubernetes CronJob: schedule: "0 0 1,15 * *" with timeZone:.
  • AWS EventBridge: cron(0 0 1,15 * ? *), with ? in the day-of-week field.

Common mistakes

  • Adding a weekday. 0 0 1,15 * 1-5 looks like "the 1st and 15th, if they're weekdays", but cron runs when either day field matches, so it runs on the 1st, the 15th and every weekday. To move weekend dates, keep 0 0 1,15 * * and let the script decide, or on AWS use two rules with 1W and 15W (the nearest weekday).
  • 0 0 1-15 * *. A dash is a range: every day from the 1st to the 15th.

See the next runs in the Cron Expression Parser.