Cron expression for the first Monday of the month
At 09:00, on Monday.
Five fields for Linux, GitHub Actions and Kubernetes (minute hour day month weekday). Six or seven for Quartz. Nothing you type leaves your device.
What it means and when to use it
Many answers online suggest 0 9 1-7 * 1 for the first Monday. It does not work, and the reason is the rule from the cron manual: when both the day-of-month field and the day-of-week field are restricted, the job runs when EITHER matches. So 0 9 1-7 * 1 runs on every day from the 1st to the 7th, and also on every Monday of the month. The correct approach is to run on every Monday (0 9 * * 1) and let the command check that the day of the month is 7 or less.
Watch out for this
The check is [ "$(date +\%d)" -le 7 ]. The percent sign needs the backslash inside a crontab. Put together: 0 9 * * 1 [ "$(date +\%d)" -le 7 ] && /home/me/bin/monthly-meeting.sh. The logic was tested on several dates: 2 November 2026, 7 December 2026 and 4 January 2027 passed as first Mondays, while 9 November and 14 December were refused. In Quartz or Spring the same schedule is a single expression: 0 0 9 ? * 2#1 (the number 2 is Monday there; the part after the hash is the first one of the month).
What each field says
| Field | Written | Means |
|---|---|---|
| Minute | 0 | 0 |
| Hour | 9 | 9 |
| Day of month | * | every day of the month |
| Month | * | every month |
| Day of week | 1 | Mon |
An example of the next runs
If it were Friday 13 November 2026 at 10:20 UTC, the next six runs would be these. Times are shown in UTC and in India time (UTC+5:30).
| UTC | India time |
|---|---|
| Mon 16 Nov, 09:00 | Mon 16 Nov, 14:30 |
| Mon 23 Nov, 09:00 | Mon 23 Nov, 14:30 |
| Mon 30 Nov, 09:00 | Mon 30 Nov, 14:30 |
| Mon 7 Dec, 09:00 | Mon 7 Dec, 14:30 |
| Mon 14 Dec, 09:00 | Mon 14 Dec, 14:30 |
| Mon 21 Dec, 09:00 | Mon 21 Dec, 14:30 |
The same schedule in other places
Quartz
Quartz has a seconds field in front, uses ? in one day field, and counts Sunday as 1:
0 0 9 ? * 2GitHub Actions
Scheduled workflows run in UTC unless you add a time zone. In the workflow file:
on:
schedule:
- cron: '0 9 * * 1'Kubernetes CronJob
In a CronJob manifest the schedule goes in spec.schedule. Without spec.timeZone, it is read in the time zone of the cluster's controller:
spec:
schedule: "0 9 * * 1"
timeZone: "Etc/UTC"A crontab line to copy
The command is made up. Replace it with your own and use full paths:
0 9 * * 1 [ "$(date +\%d)" -le 7 ] && /home/me/bin/monthly-meeting.sh >> /home/me/meeting.log 2>&1Questions
How do I get the last Friday of the month in plain cron?
Run every Friday and test that the day of the month is more than 24 and that adding seven days moves to the next month, or use Quartz 6L.
This is an independent guide and tool. It is not connected with crontab.guru, Quartz, GitHub, Kubernetes, Spring or the Linux projects named here. Product names belong to their owners.