Run Jobs on the Last Day of the Month at 9 AM | CronBase

cron expression Standard
$ 0 9 L * *

At nine o'clock in the morning on the last day of every month

The `0 9 L * *` cron expression schedules a task to run precisely at nine AM on the final day of every month, regardless of whether the month has twenty-eight, thirty, or thirty-one days. It is commonly used for generating monthly financial statements, processing end-of-month billing cycles, and compiling aggregate usage reports.

Minute
0
Hour
9
Day of Month
L
Month
*
Day of Week
*

* Tools

Code & Implementations

nodejs
const cron = require('node-cron');
const { exec } = require('child_process');

// Since node-cron doesn't natively support 'L', we run a check daily at 9:00 AM
cron.schedule('0 9 28-31 * *', () => {
    const today = new Date();
    const tomorrow = new Date(today);
    tomorrow.setDate(today.getDate() + 1);

    // If tomorrow's date is 1, then today is the last day of the month
    if (tomorrow.getDate() === 1) {
        console.log('Executing monthly report job...');
        runMonthlyJob();
    } else {
        console.log('Skipping job: Not the last day of the month.');
    }
}, {
    scheduled: true,
    timezone: "UTC"
});

function runMonthlyJob() {
    exec('/usr/local/bin/monthly-task.sh', (error, stdout, stderr) => {
        if (error) {
            console.error(`Job failed: ${error.message}`);
            return;
        }
        console.log(`Job output: ${stdout}`);
    });
}
Setup notes

Install 'node-cron' using npm, then run this script as a background daemon process using PM2 or systemd to ensure continuous execution.

Partner BetterStack

Monitor this schedule in production

Get alerted the moment this cron job fails, is late, or doesn't run. BetterStack tracks execution, duration, and output — no infrastructure required.

Platform Equivalents

Systemd Timer

OnCalendar*-*-* 09:00:00

my-task.timer
[Unit]
Description=Timer for cron expression: 0 9 L * *

[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true

[Install]
WantedBy=timers.target

Frequently Asked Questions

What happens if my cron engine does not support the 'L' character?

If your cron engine (like standard Linux crontab) does not support 'L', you must schedule the job to run daily from the 28th to the 31st using '0 9 28-31 * *' and prepend a date validation command to ensure the script only executes if tomorrow is the 1st.

How does Daylight Saving Time (DST) affect a 9:00 AM monthly execution?

If your server runs on local time zones that observe DST, the job will shift relative to UTC. To guarantee consistent 24-hour intervals and prevent reporting anomalies, run your server and cron daemon in Coordinated Universal Time (UTC).

Is '0 9 L * *' supported natively in Kubernetes CronJobs?

No, Kubernetes uses the Go-based robfig/cron parser, which does not support the 'L' wildcard. You must implement a workaround, such as scheduling the job daily between the 28th and 31st and evaluating the date inside your container entrypoint.

How can I prevent database connection pool exhaustion during this end-of-month run?

Because monthly jobs process high volumes of data, isolate the database pool for this job. Implement strict timeouts, limit concurrent queries, and use cursor-based batching instead of loading all monthly transaction records into memory at once.

How do I schedule this in Quartz or Spring Scheduler?

In Quartz and Spring, you must use a 6-field cron syntax and replace the day-of-week field with a question mark to avoid conflicts. The correct expression is '0 0 9 L * ?' to run at exactly 9:00:00 AM on the last day of every month.

* Explore

Related expressions you might need

Was this helpful?

Last verified: