Cron: Last Day of the Month
Standard POSIX cron has no direct way to say "last day of the month" — months have different lengths (28 to 31 days), and cron's day-of-month field only understands fixed numbers or ranges, not relative positions. The common workaround is 0 0 28-31 * * combined with a same-day check inside the job itself.
0 0 28-31 * *
Examples & Variations
Common Mistakes
- Assuming 0 0 31 * * runs on the last day of every month — it only fires in months that actually have 31 days, silently skipping February, April, June, September, and November.
- Copying Quartz's L syntax (0 0 L * ?) into a standard Linux crontab — Vixie cron and most POSIX cron daemons don't support L at all and will reject the entry or behave unpredictably.
- Forgetting to guard the 28-31 workaround with an actual date check — without it, the job runs on every one of those days, not just the true last day.
Best Practices
- The most portable standard-cron workaround: schedule 0 0 28-31 * * and add a check in your script — e.g. compare tomorrow's date to see if it rolls into a new month — and exit immediately if today isn't actually the last day.
- If your scheduler is Quartz, AWS EventBridge, or another system that supports the L character, use 0 0 L * ? directly instead of the day-range workaround — it's cleaner and unambiguous.
- Consider whether 'first day of next month' (0 0 1 * *, run one day later) achieves the same practical goal — it's often simpler than true last-day-of-month logic.
Frequently Asked Questions
- Why can't standard cron express 'last day of the month' directly?
- Cron's fields only support fixed values, ranges, steps, and lists — none of which can express a relative concept like 'the final day,' since that day's number (28, 29, 30, or 31) changes depending on the month and leap year.
- What is the L character in Quartz Scheduler?
- Quartz (and AWS EventBridge, which uses Quartz-style cron) extends the day-of-month field with L, meaning 'last day of the month.' 0 0 L * ? runs at midnight on the last day, whatever it is that month — but this only works in Quartz-compatible schedulers, not standard Linux cron.
- What's the simplest reliable workaround for standard cron?
- Run daily (or on 28-31) and check the date programmatically: in most languages, adding one day to today and checking if the month changed tells you unambiguously whether today is the last day of the month.