Cron for a specific weekday or day of month in CI
Cron day targeting trips people up because when both day-of-month and day-of-week are set (not *), POSIX cron runs when either matches, not both. Leave one field as * unless you truly want the OR behavior.
What this error means
A cron meant for "the 1st, only if it is a Monday" instead runs on every 1st and every Monday, because both day fields were set at once.
# Intended: first Monday only. This runs on the 1st OR any Monday.
on:
schedule:
- cron: '0 9 1 * 1'Common causes
Day-of-month and day-of-week are OR-ed
When both fields are specified, cron fires if either matches, which is rarely what "specific date and weekday" means.
Assuming AND semantics
People expect the two day fields to intersect, but POSIX cron unions them.
How to fix it
Set only one day field
- For a weekday, set day-of-week and leave day-of-month as
*. - For a day of month, set day-of-month and leave day-of-week as
*. - For combined logic, gate inside the job with a date check.
# Every Monday at 09:00 UTC
on:
schedule:
- cron: '0 9 * * 1'
# First of the month at 09:00 UTC
- cron: '0 9 1 * *'Combine date logic inside the job
For "first Monday" style rules, run daily and exit early unless a shell date check passes.
- run: |
if [ "$(date -u +%u)" != "1" ] || [ "$(date -u +%d)" -gt 07 ]; then
echo "not the first Monday"; exit 0
fiHow to prevent it
- Leave one day field as
*to avoid OR surprises. - Put complex date logic in a job-level guard.
- Test the expression against a cron calculator.