Bi-Monthly on the First at Midnight | CronBase
0 0 1 */2 * At midnight on the first day of every other month
This cron expression schedules a task to run at exactly midnight on the first day of every other month. In standard cron systems, it triggers on the first of January, March, May, July, September, and November, making it ideal for bi-monthly system maintenance, database rotations, and recurring financial reporting tasks.
- Minute
- 0
- Hour
- 0
- Day of Month
- 1
- Month
- */2
- Day of Week
- *
Next 5 Runs
- in 37d 3h
- in 98d 3h
- in 159d 3h
- in 218d 3h
- in 279d 3h
* Tools
Code & Implementations
#!/usr/bin/env bash
# Production-ready Bash cron setup for bi-monthly tasks
set -euo pipefail
# Define the cron schedule
CRON_SCHEDULE="0 0 1 */2 *"
JOB_COMMAND="/usr/local/bin/archive-old-data.sh"
# Ensure the script runs with a lock to prevent concurrent executions
# and logs outputs to a dedicated file for auditability.
echo "Installing bi-monthly cron job..."
(crontab -l 2>/dev/null | grep -v "$JOB_COMMAND"; echo "$CRON_SCHEDULE /usr/bin/flock -n /var/lock/bi_monthly_archive.lock $JOB_COMMAND >> /var/log/cron_bi_monthly.log 2>&1") | crontab -
echo "Cron job installed successfully." › Setup notes
Save this script to a file, make it executable using chmod +x, and run it to register the bi-monthly task securely in your crontab.
const cron = require('node-cron');
const { exec } = require('child_process');
// Schedule bi-monthly task at 00:00 on the 1st of every other month
// Expression: 0 0 1 */2 *
const schedule = '0 0 1 */2 *';
const task = cron.schedule(schedule, async () => {
console.log(`[${new Date().toISOString()}] Starting bi-monthly maintenance task...`);
try {
// Execute the maintenance task with proper error handling and timeout
await runMaintenance();
console.log(`[${new Date().toISOString()}] Bi-monthly task completed successfully.`);
} catch (error) {
console.error(`[${new Date().toISOString()}] CRITICAL: Bi-monthly task failed:`, error);
// In production, trigger an alert to PagerDuty, Slack, or Sentry here
}
}, {
scheduled: true,
timezone: "UTC" // Force UTC to avoid Daylight Saving Time issues
});
async function runMaintenance() {
return new Promise((resolve, reject) => {
exec('/usr/local/bin/bi-monthly-cleanup.sh', (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
} › Setup notes
Install node-cron via npm using npm install node-cron, then run this script using Node.js to start the background scheduler daemon.
import logging
import sys
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[logging.StreamHandler(sys.stdout)]
)
def run_bi_monthly_job():
logging.info("Starting bi-monthly data rotation job.")
try:
# Business logic goes here
logging.info("Bi-monthly data rotation job completed successfully.")
except Exception as e:
logging.error(f"Failed to execute bi-monthly job: {str(e)}", exc_info=True)
if __name__ == "__main__":
scheduler = BlockingScheduler()
# CronTrigger corresponds to standard "0 0 1 */2 *"
# Executing at midnight (00:00) on the 1st day of every 2nd month
trigger = CronTrigger(
month='1,3,5,7,9,11',
day=1,
hour=0,
minute=0,
timezone='UTC'
)
scheduler.add_job(
run_bi_monthly_job,
trigger=trigger,
id='bi_monthly_maintenance',
replace_existing=True
)
logging.info("Starting scheduler for bi-monthly maintenance...")
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
logging.info("Scheduler stopped.") › Setup notes
Install the required APScheduler library via pip install apscheduler and run this Python script to start the blocking scheduler.
package main
import (
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/robfig/cron/v3"
)
func main() {
logger := log.New(os.Stdout, "[BI-MONTHLY-JOB] ", log.LstdFlags|log.Lshortfile)
nyc, err := time.LoadLocation("UTC")
if err != nil {
logger.Fatalf("Failed to load UTC timezone: %v", err)
}
c := cron.New(cron.WithLocation(nyc))
_, err = c.AddFunc("0 0 1 */2 *", func() {
logger.Println("Starting bi-monthly database optimization...")
defer func() {
if r := recover(); r != nil {
logger.Printf("CRITICAL: Panic recovered in job: %v", r)
}
}()
if err := executeMaintenance(); err != nil {
logger.Printf("ERROR: Maintenance task failed: %v", err)
} else {
logger.Println("Bi-monthly database optimization completed successfully.")
}
})
if err != nil {
logger.Fatalf("Failed to schedule cron job: %v", err)
}
c.Start()
logger.Println("Scheduler started successfully. Waiting for execution...")
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
logger.Println("Stopping scheduler gracefully...")
c.Stop()
logger.Println("Scheduler stopped.")
}
func executeMaintenance() error {
time.Sleep(5 * time.Second)
return nil
} › Setup notes
Initialize a Go module, run go get github.com/robfig/cron/v3 to install the cron library, and run this code with go run main.go.
package com.example.scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Component
public class BiMonthlyMaintenanceScheduler {
private static final Logger logger = LoggerFactory.getLogger(BiMonthlyMaintenanceScheduler.class);
/**
* Executes a bi-monthly maintenance task at midnight on the first day of every other month.
* Cron expression: 0 0 1 */2 *
* timezone is set to UTC to guarantee consistent execution times.
*/
@Scheduled(cron = "0 0 1 */2 *", zone = "UTC")
public void executeBiMonthlyTask() {
logger.info("Bi-monthly maintenance process started at {}", Instant.now());
try {
runMaintenanceRoutine();
logger.info("Bi-monthly maintenance process completed successfully.");
} catch (Exception ex) {
logger.error("CRITICAL: Bi-monthly maintenance failed to execute", ex);
sendAlertNotification(ex.getMessage());
}
}
private void runMaintenanceRoutine() throws Exception {
Thread.sleep(10000);
}
private void sendAlertNotification(String errorMessage) {
// Code to send alerts to operations team
}
} › Setup notes
Add @EnableScheduling to your main Spring Boot Application class and place this class in your application context package.
apiVersion: batch/v1
kind: CronJob
metadata:
name: bimonthly-data-archiver
namespace: maintenance
spec:
# Run at midnight on the first day of every other month (0 0 1 */2 *)
schedule: "0 0 1 */2 *"
timeZone: "Etc/UTC"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
template:
spec:
containers:
- name: archiver
image: postgres:15-alpine
command:
- /bin/sh
- -c
- |
set -e
echo "Starting bi-monthly archive process..."
psql -h $DB_HOST -U $DB_USER -d prod_db -c "SELECT rotate_partitions();"
echo "Archive process completed successfully."
env:
- name: DB_HOST
value: "postgres-service.database.svc.cluster.local"
- name: DB_USER
value: "postgres"
restartPolicy: OnFailure › Setup notes
Save this YAML configuration to a file named cronjob.yaml and apply it to your cluster using kubectl apply -f cronjob.yaml.
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 0 1 */2 ? *) Systemd Timer
OnCalendar*-00/2-01 00:00:00
[Unit]
Description=Timer for cron expression: 0 0 1 */2 *
[Timer]
OnCalendar=*-00/2-01 00:00:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
Which months will this cron expression actually trigger on?
This expression triggers on the first day of every second month starting from January. Specifically, it executes on January 1st, March 1st, May 1st, July 1st, September 1st, and November 1st.
How does Daylight Saving Time affect this midnight schedule?
If your system timezone is set to a local time that observes DST, the midnight execution could run twice or be skipped entirely during clock changes. To prevent this, configure your cron daemon or scheduler to use UTC.
What is the best way to test a cron job that runs only every two months?
Since waiting two months is impractical, test your job by temporarily changing the cron pattern to run every minute (e.g., `* * * * *`) in a staging environment. Additionally, write unit tests to verify the job's core business logic independently of the cron schedule.
Can I stagger this job to avoid the standard midnight resource spike?
Yes. Many system jobs run exactly at midnight on the 1st of the month, causing database and network congestion. You can stagger this job by shifting it to run at a lower-traffic time, such as 3:30 AM (`30 3 1 */2 *`).
How should I monitor this job since it runs so infrequently?
Passive monitoring (waiting for it to fail) is highly risky for infrequent jobs. Implement active monitoring using 'Dead Man's Snitch' or healthcheck endpoints. If the external monitoring service doesn't receive a ping from your job within the expected window, it triggers an alert.
* Explore
Related expressions you might need
Last verified: