The expression
0 0 1 * *
Runs once a month, at 00:00 on the 1st. The @monthly macro means exactly this, so these crontab lines are the same:
@monthly /usr/local/bin/monthly-report.sh
0 0 1 * * /usr/local/bin/monthly-report.sh
| Field | Value | Meaning |
|---|---|---|
| Minute | 0 |
at minute 0 |
| Hour | 0 |
at midnight |
| Day of month | 1 |
the 1st |
| Month | * |
every month |
| Day of week | * |
any day of the week |
Picking the day
A monthly job only needs a single day of the month plus a fixed minute and hour. The 1st is the convention, not a rule:
| Schedule | Expression |
|---|---|
1st at midnight (@monthly) |
0 0 1 * * |
| The 5th at 9am | 0 9 5 * * |
| The 28th at 3am (exists in every month) | 0 3 28 * * |
| Last day of the month | 0 0 28-31 * * plus a test |
| Twice a month | 0 0 1,15 * * |
| Every three months | 0 0 1 1,4,7,10 * |
Days 1 to 28 occur in every month. Pick a day above 28 and cron simply skips the months that don't have it: 0 0 31 * * runs only seven times a year, and 0 0 30 * * never runs in February. Cron doesn't move the run to the nearest valid day.
Where to write it
- crontab (Linux, macOS):
@monthly commandor0 0 1 * * commandincrontab -e, in the server's time zone. Many distributions also have/etc/cron.monthly/, run by anacron, which catches up when the machine was off on the 1st. - GitHub Actions:
cron: '0 0 1 * *'in UTC; macros aren't accepted. A monthly job in a quiet public repository can stop silently: GitHub disables scheduled workflows after 60 days without repository activity, which is only two runs. - Kubernetes CronJob:
schedule: "@monthly"or"0 0 1 * *", withtimeZone:. SetstartingDeadlineSecondsso a run missed during an outage is still started later. - AWS EventBridge:
cron(0 0 1 * ? *). There israte(30 days)too, but that is every 30 days from the rule's creation, drifting a day or two against the calendar every month. For "monthly", use the cron form.
Common mistakes
* * 1 * *runs every minute on the 1st: 1,440 runs.0 0 * 1 *is every day in January, not once a month. The month field restricts months; the day-of-month field picks the day.- A day that doesn't exist.
0 0 31 * *skips April, June, September, November and February. - Nobody notices it failed. A job that runs twelve times a year has twelve chances to be seen. Alert on failure, or on a missing run, rather than relying on someone checking.
See the next twelve runs in the Cron Expression Parser.