0 0 1 */4 * Cron Schedule Reference | CronBase
0 0 1 */4 * At midnight on the first day of every fourth month, specifically in January, May, and September.
This cron expression schedules a task to run at midnight on the first day of every fourth month. In standard cron environments, this translates to executing at 12:00 AM on January 1st, May 1st, and September 1st, providing a reliable triannual cadence for heavy maintenance and long-term data archiving.
- Minute
- 0
- Hour
- 0
- Day of Month
- 1
- Month
- */4
- Day of Week
- *
Next 5 Runs
- in 37d 3h
- in 159d 3h
- in 279d 3h
- in 402d 3h
- in 524d 3h
* Tools
Code & Implementations
#!/usr/bin/env bash
set -euo pipefail
# Production-grade script to safely install the triannual cron job
CRON_JOB="0 0 1 */4 * /usr/local/bin/triannual-cleanup.sh >> /var/log/cleanup.log 2>&1"
# Verify the target script exists before scheduling
if [ ! -f /usr/local/bin/triannual-cleanup.sh ]; then
echo "Warning: Target script /usr/local/bin/triannual-cleanup.sh does not exist yet."
fi
# Check if the cron job already exists to prevent duplicate entries
if crontab -l 2>/dev/null | grep -Fq "/usr/local/bin/triannual-cleanup.sh"; then
echo "Cron job already exists. Skipping deployment."
else
(crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab -
echo "Successfully scheduled triannual cleanup job in crontab."
fi › Setup notes
Save the code block as a shell script, make it executable with chmod +x, and run it with appropriate permissions to modify the crontab.
const cron = require('node-cron');
const winston = require('winston'); // Production logger
const logger = winston.createLogger({
level: 'info',
transports: [new winston.transports.Console()]
});
// Expression: 0 0 1 */4 * (At midnight on day 1 of every 4th month)
const CRON_SCHEDULE = '0 0 1 */4 *';
try {
const task = cron.schedule(CRON_SCHEDULE, async () => {
const startTime = Date.now();
logger.info('Starting triannual database archiving task...');
try {
// Perform heavy historical database archiving and index management
await runArchivingPipeline();
const duration = Date.now() - startTime;
logger.info(`Archiving completed successfully in ${duration}ms`);
} catch (error) {
logger.error('Critical failure in triannual archiving job:', error);
// Route to your incident response system (e.g., PagerDuty, Opsgenie)
await alertOpsTeam(error);
}
}, {
scheduled: true,
timezone: "UTC" // Enforce UTC to avoid daylight saving shifts
});
task.start();
logger.info('Triannual scheduler initialized successfully.');
} catch (initError) {
logger.error('Failed to initialize triannual cron task:', initError);
process.exit(1);
}
async function runArchivingPipeline() { /* Real business logic goes here */ }
async function alertOpsTeam(err) { /* Notification integration */ } › Setup notes
Install node-cron and winston via npm: npm install node-cron winston. Run this script as a daemon or background process.
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')
logger = logging.getLogger('TriannualScheduler')
def execute_triannual_audit():
logger.info("Initiating triannual audit and compliance report generation...")
try:
# Place long-running audit logic here
logger.info("Triannual audit completed successfully.")
except Exception as e:
logger.error(f"Fatal error during triannual execution: {str(e)}", exc_info=True)
# Route to your alerting service
sys.exit(1)
if __name__ == "__main__":
scheduler = BlockingScheduler()
# 0 0 1 */4 * -> equivalent to months 1, 5, 9
trigger = CronTrigger(
month='1,5,9',
day=1,
hour=0,
minute=0,
timezone='UTC'
)
scheduler.add_job(execute_triannual_audit, trigger=trigger, id='triannual_audit_job')
logger.info("Scheduler started. Monitoring triannual job...")
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
logger.info("Scheduler stopped cleanly.") › Setup notes
Install APScheduler using pip install apscheduler. Run the Python script to maintain a persistent scheduling process.
package main
import (
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/robfig/cron/v3"
)
func runTriannualReport() {
log.Println("Starting triannual financial consolidation...")
// Perform actual business logic here
log.Println("Triannual financial consolidation completed.")
}
func main() {
// Load UTC location to avoid timezone offsets on production servers
utc, err := time.LoadLocation("UTC")
if err != nil {
log.Fatalf("Failed to load UTC timezone: %v", err)
}
c := cron.New(cron.WithLocation(utc))
// Standard cron expression: 0 0 1 */4 *
cronSpec := "0 0 1 */4 *"
_, err = c.AddFunc(cronSpec, func() {
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in triannual job: %v", r)
}
}()
runTriannualReport()
})
if err != nil {
log.Fatalf("Error scheduling triannual job: %v", err)
}
c.Start()
log.Println("Triannual scheduler started successfully under UTC.")
// Handle clean shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
log.Println("Shutting down scheduler...")
c.Stop()
} › Setup notes
Initialize a Go module, run go get github.com/robfig/cron/v3, paste the code, and compile or run it using 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;
@Component
public class TriannualMaintenanceScheduler {
private static final Logger logger = LoggerFactory.getLogger(TriannualMaintenanceScheduler.class);
// Spring cron: "second minute hour day-of-month month day-of-week"
// We enforce UTC to ensure consistent behavior across distributed cloud nodes
@Scheduled(cron = "0 0 0 1 */4 *", zone = "UTC")
public void executeTriannualTask() {
logger.info("Starting triannual system-wide maintenance and data purging...");
try {
processPurgePipeline();
logger.info("Triannual maintenance completed successfully.");
} catch (Exception e) {
logger.error("Critical failure during triannual maintenance execution", e);
// Standard practice: trigger external alerts or dead-man switch notifications
}
}
private void processPurgePipeline() {
// Production-grade logic for purging old partition indexes
}
} › Setup notes
Ensure @EnableScheduling is added to your Spring Boot application's configuration classes. Add this component to your package scan path.
apiVersion: batch/v1
kind: CronJob
metadata:
name: triannual-data-archiver
namespace: production
labels:
app: triannual-archiver
tier: backend
spec:
schedule: "0 0 1 */4 *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 14400 # 4 hours to recover from cluster downtime
jobTemplate:
spec:
template:
metadata:
labels:
app: triannual-archiver
spec:
restartPolicy: OnFailure
containers:
- name: archiver
image: registry.internal.net/data/archiver:v2.4.1
env:
- name: TZ
value: "UTC"
resources:
limits:
cpu: "1000m"
memory: "2Gi"
requests:
cpu: "250m"
memory: "512Mi" › Setup notes
Apply this manifest using kubectl apply -f cronjob.yaml after updating the image repository and resource allocations as needed.
Last verified:
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
AWS EventBridge
Standard cron expressions often need conversion for AWS EventBridge schedules.
cron(0 0 1 */4 ? *) Systemd Timer
OnCalendar*-00/4-01 00:00:00
[Unit]
Description=Timer for cron expression: 0 0 1 */4 *
[Timer]
OnCalendar=*-00/4-01 00:00:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
Which specific months will this cron expression execute in?
This schedule executes on the first day of January (month 1), May (month 5), and September (month 9). This is because the step value `*/4` starts at 1 and increments by 4.
How can I safely test a job that runs only once every four months?
Do not wait for the natural schedule. Test your code by triggering the execution script manually in a staging environment, or use tools to mock the system time to verify the scheduler behavior.
What timezone-related issues should I watch out for?
Standard cron runs on the host system's local time. If local time is used, transitions to and from Daylight Saving Time can shift the execution hour. Running servers in UTC mitigates this issue.
How should I set up monitoring for such an infrequent job?
Traditional threshold alerts are ineffective. Use an external monitoring service (a 'dead-man's switch') that expects a ping on the scheduled day, alerting you if that ping does not arrive within a specific window.
What happens if the host server is offline at midnight on the execution day?
Standard cron will miss the execution entirely and wait another four months. For critical jobs, use an orchestrator like Kubernetes with a `startingDeadlineSeconds` policy or systemd timers with persistent settings.
* Explore
Related expressions you might need
Last verified: