Cron First Day of Month
A cron first day of month schedule puts 1 in the day-of-month field: 0 0 1 * *. Pick a different day or time below, and see how to handle the last day of the month.
The short answer
The cron first day of month expression is 0 0 1 * *: minute 0, hour 0, day of month 1, any month, any weekday. It runs at midnight on the 1st of every month.
0 0 1 * *Runs at 12:00 AM on day 1 of every month.
Pick the day and the time
Choose the day of the month and the time. "The last day" needs a platform that supports it, and the picker shows the closest Linux option when it does not.
Cron first day of month: 0 0 1 * *
Put the day number in the third field. Every month has a 1st, so this never skips. Change the minute and hour to move the time: 0 2 1 * * is 2:00 AM on the 1st. Because the weekday field is *, it does not matter which weekday the 1st falls on.
Other monthly schedules
A list gives several days, and a list in the month field gives quarters or other patterns.
| Expression | What it does | Use |
|---|---|---|
0 2 1 * * | Runs at 2:00 AM on day 1 of every month. The 1st at 2 AM, a quiet hour for billing or reports. | Open |
0 9 15 * * | Runs at 9:00 AM on day 15 of every month. The 15th at 9 AM. | Open |
0 0 1,15 * * | Runs at 12:00 AM on days 1 and 15 of every month. Twice a month. | Open |
0 0 1 */3 * | Runs at 12:00 AM on day 1 of every month in January, April, July and October. Quarterly: January, April, July and October. | Open |
0 0 1 1 * | Runs at 12:00 AM on day 1 of every month in January. Once a year, on January 1. | Open |
0 0 28 * * | Runs at 12:00 AM on day 28 of every month. The 28th exists in every month. | Open |
The last day of the month
The last day varies between 28 and 31, and standard cron has no way to say it. Platforms that add the L character do: Quartz and Spring use 0 0 0 L * ? and EventBridge uses cron(0 0 L * ? *).
On Linux, the standard workaround is to run on the days that could be last and test whether tomorrow is the 1st. CronWave generates this line:
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/commandRuns the command only when tomorrow is the 1st, so it fires once, on the last day. It needs GNU date, and the backslash before the percent sign is required in a crontab.
Days that do not exist in every month
Cron skips a day that is not in the calendar. 0 0 31 * * is not "the last day": Day 31 does not exist in every month, so some months are skipped. So it fires in seven months a year. Use 28 or earlier for a date that must exist in every month, or use the last-day method above.
First weekday or first Monday of the month
Quartz and EventBridge can ask for these directly, for example 0 0 9 1W * ? for the weekday nearest the 1st (Runs at 9:00 AM on the weekday nearest day 1 of every month) or MON#1 for the first Monday. Linux cannot: restricting both the day of month and the day of week runs the job when either matches. The every weekday page explains the trap and the date-check workaround.
First day of the month on each platform
| Platform | Expression | Note |
|---|---|---|
| Linux / Unix | 0 0 1 * * | @monthly also works. Linux guide |
| Kubernetes | 0 0 1 * * | @monthly is the same schedule. Kubernetes guide |
| GitHub Actions | 0 0 1 * * | UTC unless the timezone key is set. GitHub Actions guide |
| Vercel | 0 0 1 * * | Works on every plan. Remember that the day-of-week field must stay *. Vercel guide |
| AWS EventBridge | cron(0 0 1 * ? *) | Needs the ? and a year. AWS guide |
| Quartz | 0 0 0 1 * ? | Seconds first, and ? in the weekday field. Quartz guide |
Month boundaries depend on the time zone
Midnight UTC on the 1st is still the last evening of the previous month in the Americas. If a monthly report must cover a local calendar month, schedule it a few hours after the boundary, or use a platform that lets you set the time zone. The cron timezone converter shows what a UTC schedule means locally.
Sources and further reading
Use a tool
Try the schedule from this page in a tool.