Run Jobs Monthly on the First Day at 1 AM | CronBase
0 1 1 * * At one o'clock in the morning on the first calendar day of every month.
This standard cron expression executes a scheduled task once a month at exactly one AM on the first day of the month. It is commonly deployed in production environments to automate monthly billing cycles, compile system performance reports, rotate database partition tables, and trigger recurring subscription renewals.
- Minute
- 0
- Hour
- 1
- Day of Month
- 1
- Month
- *
- Day of Week
- *
Next 5 Runs
- in 6d 4h
- in 37d 4h
- in 67d 4h
- in 98d 4h
- in 128d 4h
* Tools
Code & Implementations
#!/usr/bin/env bash
set -euo pipefail
# Production wrapper script for monthly database archival
# Designed to be triggered by cron: 0 1 1 * *
LOG_FILE="/var/log/db_archive_monthly.log"
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Starting monthly database archival..." >> "$LOG_FILE"
if ! pg_dump -U db_user -h localhost prod_db | gzip > "/backups/db_backup_$(date +'%Y-%m-%d').sql.gz"; then
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] ERROR: Backup failed!" >&2
exit 1
fi
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Archival completed successfully." >> "$LOG_FILE" › Setup notes
Save this script to /usr/local/bin/monthly_archive.sh, make it executable using chmod +x, and add '0 1 1 * * /usr/local/bin/monthly_archive.sh' to your system crontab.
const cron = require('node-cron');
const logger = require('./utils/logger');
const { processMonthlyBilling } = require('./services/billing');
// Schedule: 0 1 1 * * (At 01:00 on day-of-month 1)
const monthlyBillingJob = cron.schedule('0 1 1 * *', async () => {
logger.info('Starting monthly billing job execution...');
try {
const result = await processMonthlyBilling();
logger.info(`Monthly billing completed. Invoices processed: ${result.processedCount}`);
} catch (error) {
logger.error('Critical failure in monthly billing job:', error);
}
}, {
scheduled: true,
timezone: "UTC"
});
monthlyBillingJob.start(); › Setup notes
Install the node-cron library via npm install node-cron, and run this script as a long-running background daemon under a process manager like PM2.
import logging
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from my_app.tasks import generate_monthly_reports
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def execute_monthly_report_generation():
logger.info("Triggering monthly report generation task...")
try:
report_path = generate_monthly_reports()
logger.info(f"Monthly reports successfully generated at: {report_path}")
except Exception as e:
logger.error(f"Failed to generate monthly reports: {str(e)}", exc_info=True)
if __name__ == "__main__":
scheduler = BlockingScheduler(timezone="UTC")
scheduler.add_job(
execute_monthly_report_generation,
'cron',
day=1,
hour=1,
minute=0,
id='monthly_reporting_job'
)
logger.info("Scheduler initialized. Monthly reporting job registered.")
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
logger.info("Scheduler shutting down gracefully.") › Setup notes
Install APScheduler via pip install apscheduler, ensure your system timezone is aligned, and run this Python script as a persistent background service.
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/robfig/cron/v3"
)
func main() {
logger := log.New(os.Stdout, "[MonthlyJob] ", log.LstdFlags|log.Lshortfile)
c := cron.New(cron.WithLocation(time.UTC))
_, err := c.AddFunc("0 1 1 * *", func() {
logger.Println("Executing monthly system reconciliation...")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour)
defer cancel()
if err := runReconciliation(ctx); err != nil {
logger.Printf("ERROR: Reconciliation failed: %v\n", err)
} else {
logger.Println("Reconciliation completed successfully.")
}
})
if err != nil {
logger.Fatalf("Failed to schedule cron job: %v", err)
}
c.Start()
logger.Println("Cron scheduler started successfully.")
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
logger.Println("Shutting down cron scheduler...")
c.Stop()
}
func runReconciliation(ctx context.Context) error {
time.Sleep(5 * time.Second)
return nil
} › Setup notes
Create a Go module, run go get github.com/robfig/cron/v3, compile the binary using go build, and execute it as a systemd service.
package com.example.scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MonthlyMaintenanceScheduler {
private static final Logger logger = LoggerFactory.getLogger(MonthlyMaintenanceScheduler.class);
/**
* Cron Expression: 0 1 1 * *
* Runs at 01:00 AM on the 1st of every month.
* Force UTC timezone to ensure consistent execution regardless of host OS timezone.
*/
@Scheduled(cron = "0 0 1 1 * *", zone = "UTC")
public void runMonthlyCleanup() {
logger.info("Starting monthly database table partition maintenance...");
try {
performPartitionCleanup();
logger.info("Monthly partition maintenance completed successfully.");
} catch (Exception e) {
logger.error("Failed to complete monthly cleanup tasks: ", e);
}
}
private void performPartitionCleanup() throws Exception {
Thread.sleep(10000);
}
} › Setup notes
Annotate your main Spring Boot class with @EnableScheduling, place this component in your project, and ensure the application context is active.
apiVersion: batch/v1
kind: CronJob
metadata:
name: monthly-data-archiver
namespace: production
labels:
app: data-archiver
tier: backend
spec:
schedule: "0 1 1 * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 1800
jobTemplate:
spec:
template:
metadata:
labels:
app: data-archiver
spec:
restartPolicy: OnFailure
containers:
- name: archiver-task
image: registry.example.com/data-tools:v2.1.0
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "/app/run-archival.sh"]
env:
- name: DB_HOST
value: "postgres-primary.production.svc.cluster.local"
resources:
limits:
cpu: "1000m"
memory: "2Gi"
requests:
cpu: "500m"
memory: "1Gi" › Setup notes
Apply this manifest to your cluster using kubectl apply -f manifest.yaml. Monitor execution with kubectl get cronjob -n production.
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
AWS EventBridge
Standard cron expressions often need conversion for AWS EventBridge schedules.
cron(0 1 1 * ? *) Systemd Timer
OnCalendar*-*-01 01:00:00
[Unit]
Description=Timer for cron expression: 0 1 1 * *
[Timer]
OnCalendar=*-*-01 01:00:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
What happens if the server is down at 1:00 AM on the first of the month?
If your system is offline during the execution window, standard cron will miss the run entirely. To prevent skipped monthly jobs, use an orchestrator like Kubernetes with a startingDeadlineSeconds policy, or use tools like Anacron which detect missed executions and run them immediately upon system startup.
How does Daylight Saving Time (DST) affect the 1:00 AM monthly schedule?
If your system is configured to use a local timezone that observes DST, the job could run twice or be skipped when clocks transition. To avoid this operational hazard, configure your cron daemon, application scheduler, or Kubernetes cluster to run strictly in UTC.
Can I run this job on the last day of the month instead of the first?
Standard cron syntax does not native support 'L' (last day). To target the last day of the month, you must schedule the job to run daily during the end-of-month window (e.g., days 28-31) and include a wrapper script that checks if tomorrow is the first day of the next month.
Why should I run a monthly job at 1:00 AM instead of midnight (00:00)?
Running jobs at exactly midnight on the first of the month often conflicts with system-level rollover tasks, database log rotations, and third-party API batch routines. Scheduling at 1:00 AM provides a buffer, reducing resource contention and preventing race conditions.
How do I test a monthly cron job locally without waiting a month?
You can test the execution logic by temporarily changing the cron schedule in your local configuration to run every minute (* * * * *). Alternatively, configure your task runner to allow manual triggering (ad-hoc execution) via an admin CLI command or API endpoint.
* Explore
Related expressions you might need
Last verified: