Run on the First Monday of the Month | CronBase
0 9 * * 1#1 At nine o'clock in the morning on the first Monday of every month.
The `0 9 * * 1#1` cron expression schedules a task to execute at exactly 9:00 AM on the first Monday of every month. It is commonly used for generating monthly business reports, triggering compliance audits, or kicking off payroll processing workflows at the start of the first work week.
- Minute
- 0
- Hour
- 9
- Day of Month
- *
- Month
- *
- Day of Week
- 1#1
Next 5 Runs
- in 1d 12h
- in 8d 12h
- in 15d 12h
- in 22d 12h
- in 29d 12h
* Tools
Code & Implementations
#!/usr/bin/env bash
set -euo pipefail
# Since standard Vixie cron does not support the '#' symbol natively,
# we schedule the cron job to run every Monday at 9:00 AM:
# 0 9 * * 1 /path/to/wrapper.sh
# Inside the script, we verify if the current day of the month is 7 or less.
CURRENT_DAY=$(date +%-d)
if [ "$CURRENT_DAY" -le 7 ]; then
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Info: First Monday detected. Executing production task..."
# Place your actual production command or script invocation here
/usr/local/bin/run-monthly-reports.sh
else
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Info: Today is day $CURRENT_DAY of the month. Skipping run."
exit 0
fi › Setup notes
Save this script to your server, make it executable with chmod +x, and register it in your crontab using 0 9 * * 1 /path/to/script.sh to emulate the first-Monday-of-the-month behavior reliably.
const cron = require('node-cron');
// Node-cron does not natively parse the '#' modifier.
// We schedule the callback to run every Monday at 9:00 AM
// and programmatically guard the execution to the first 7 days of the month.
cron.schedule('0 9 * * 1', () => {
const now = new Date();
const dayOfMonth = now.getDate();
if (dayOfMonth <= 7) {
console.log(`[${now.toISOString()}] Executing monthly report generation...`);
try {
// Insert your business logic, database queries, or API calls here
} catch (error) {
console.error('Failed to run monthly job:', error);
}
} else {
console.log(`[${now.toISOString()}] Skipping execution: Day of month is ${dayOfMonth}.`);
}
}, {
timezone: "America/New_York"
}); › Setup notes
Install node-cron via npm. This script schedules a weekly check on Mondays at 9:00 AM EST/EDT, executing your logic only if it is the first Monday of the month.
from datetime import datetime
import sys
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def run_monthly_job():
today = datetime.now()
# In Python's weekday(), 0 is Monday.
# The first Monday of the month must fall on days 1 through 7.
if today.weekday() == 0 and today.day <= 7:
logging.info("Starting first Monday of the month automated run...")
try:
# Place production logic here
pass
except Exception as e:
logging.error(f"Execution failed: {str(e)}")
sys.exit(1)
else:
logging.info(f"Skipping run: Today is weekday {today.weekday()} and day {today.day}.")
if __name__ == "__main__":
run_monthly_job() › Setup notes
Run this script via an external scheduler (like Jenkins or standard system cron) configured to run every Monday at 9:00 AM (0 9 * * 1).
package main
import (
"fmt"
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() {
// Use UTC or load a specific location like America/New_York
nyc, err := time.LoadLocation("America/New_York")
if err != nil {
log.Fatalf("Failed to load timezone: %v", err)
}
c := cron.New(cron.WithLocation(nyc))
// Schedule for every Monday at 9:00 AM
_, err = c.AddFunc("0 9 * * 1", func() {
now := time.Now().In(nyc)
if now.Day() <= 7 {
log.Println("Running first Monday of the month database maintenance...")
// Execute business logic
} else {
log.Printf("Skipping: Day of month is %d", now.Day())
}
})
if err != nil {
log.Fatalf("Error scheduling job: %v", err)
}
c.Start()
select {} // Block indefinitely
} › Setup notes
Run go get github.com/robfig/cron/v3 to install the dependency, then execute this Go application as a background daemon.
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class MonthlyScheduler {
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
JobDetail job = JobBuilder.newJob(MonthlyTask.class)
.withIdentity("monthlyTask", "group1")
.build();
// Quartz scheduler natively supports the '#' operator.
// Syntax: Seconds, Minutes, Hours, Day-of-Month, Month, Day-of-Week
// Note: In Quartz, Day-of-Week 2 is Monday (1 is Sunday).
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("firstMondayTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * 2#1"))
.build();
scheduler.scheduleJob(job, trigger);
}
public static class MonthlyTask implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Executing Quartz job on the first Monday of the month!");
// Execute enterprise logic here
}
}
} › Setup notes
Add the Quartz Scheduler dependency to your Maven or Gradle project. This Java implementation natively supports the # expression without programmatic date checks.
apiVersion: batch/v1
kind: CronJob
metadata:
name: first-monday-monthly-job
namespace: default
spec:
schedule: "0 9 * * 1" # Run every Monday at 9:00 AM
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: worker
image: alpine:3.18
command:
- /bin/sh
- -c
- |
set -e
DAY_OF_MONTH=$(date +%-d)
if [ "$DAY_OF_MONTH" -le 7 ]; then
echo "It is the first Monday of the month (Day $DAY_OF_MONTH). Running job..."
# Invoke your actual container workload here
else
echo "Skipping run: today is day $DAY_OF_MONTH of the month."
fi
restartPolicy: OnFailure › Setup notes
Apply this manifest to your cluster using kubectl apply -f manifest.yaml. The container checks the date and safely exits early if it is not the first Monday of the month.
Last verified:
Keep this cron job monitored 24/7
UptimeRobot alerts you the moment a scheduled job stops responding. Free plan monitors up to 50 endpoints — no credit card required.
Platform Equivalents
Systemd Timer
OnCalendarMon *-*-* 09:00:00
[Unit]
Description=Timer for cron expression: 0 9 * * 1#1
[Timer]
OnCalendar=Mon *-*-* 09:00:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
What happens if the first Monday falls on a public holiday?
Standard cron schedulers do not have awareness of regional holidays. The job will execute unconditionally. To handle holidays, your application logic must check a calendar API or database and defer execution if necessary.
Does standard Linux crontab support the # syntax natively?
No, standard Vixie or ISC cron used in most Linux distributions does not support the `#` character. You must schedule the job to run every Monday and use a shell condition like `[ $(date +\%d) -le 7 ]` to restrict execution to the first Monday.
How do daylight saving time (DST) shifts affect this monthly schedule?
If your server timezone observes DST, the 9:00 AM execution time will shift relative to UTC. To prevent issues where a job runs twice or is skipped during a transition, configure your cron daemon or orchestrator to run in UTC.
How can I test a job that only runs once a month?
Do not wait for the first Monday to test. Implement a dry-run flag in your application code, or trigger the job manually in your staging environment using a temporary cron schedule like `*/5 * * * *` to verify the execution path.
What is the best way to monitor this highly infrequent job?
Since this job runs only twelve times a year, passive monitoring is insufficient. Use a "dead man's switch" monitor (like Cronitor or Healthchecks.io) that expects a ping at 9:00 AM on the first Monday and alerts if the ping is missed.
* Explore
Related expressions you might need
Last verified: