Cron expression for the last day of the month
At 23:00, on days 28 to 31 of the month.
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
Plain five-field cron cannot say "last day of the month". The common pattern runs the job on days 28, 29, 30 and 31, and lets a small test in the command decide whether today is really the last day: it asks for tomorrow's day of the month and proceeds only if that is 01. With GNU date the test is [ "$(date -d tomorrow +\%d)" = 01 ]. Quartz has it built in: 0 0 23 L * ? means 23:00 on the last day, and L-2 means two days before the last day.
Watch out for this
Two things must be right. First, escape the percent sign with a backslash inside a crontab, because cron turns an unescaped % into a newline (the manual describes this), which would cut your command in two. Second, date -d tomorrow is GNU syntax: it works on Linux and in Git Bash, but not on macOS, where the command is date -v+1d +%d. This was run for several dates, including 28 and 29 February 2028, and printed LAST only for the real last days.
What each field says
| Field | Written | Means |
|---|---|---|
| Minute | 0 | 0 |
| Hour | 23 | 23 |
| Day of month | 28-31 | 28, 29, 30, 31 |
| Month | * | every month |
| Day of week | * | every day of the week |
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 |
|---|---|
| Sat 28 Nov, 23:00 | Sun 29 Nov, 04:30 |
| Sun 29 Nov, 23:00 | Mon 30 Nov, 04:30 |
| Mon 30 Nov, 23:00 | Tue 1 Dec, 04:30 |
| Mon 28 Dec, 23:00 | Tue 29 Dec, 04:30 |
| Tue 29 Dec, 23:00 | Wed 30 Dec, 04:30 |
| Wed 30 Dec, 23:00 | Thu 31 Dec, 04: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 23 28-31 * ?GitHub Actions
Scheduled workflows run in UTC unless you add a time zone. In the workflow file:
on:
schedule:
- cron: '0 23 28-31 * *'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 23 28-31 * *"
timeZone: "Etc/UTC"A crontab line to copy
The command is made up. Replace it with your own and use full paths:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /home/me/bin/month-end.sh >> /home/me/month-end.log 2>&1Questions
Does GitHub Actions support L?
No. GitHub documents the five POSIX fields only. Use days 28-31 and test the date inside the job.
What about Kubernetes?
Same: standard five fields, no L. Test the date inside the container command.
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.