Schedule Daily 2:30 AM Maintenance Tasks | CronBase
30 2 * * * Every day at two thirty in the morning
This cron expression schedules a task to execute every single day at exactly 2:30 AM. It is commonly used in production environments for off-peak operations, such as daily database backups, system cleanup scripts, and log rotations, ensuring these resource-heavy jobs run when user traffic is at its lowest point.
- Minute
- 30
- Hour
- 2
- Day of Month
- *
- Month
- *
- Day of Week
- *
Next 5 Runs
- in 5h 50m
- in 1d 5h
- in 2d 5h
- in 3d 5h
- in 4d 5h
* Tools
Code & Implementations
#!/usr/bin/env bash
# Production-ready backup script meant to be invoked by cron at 30 2 * * *
set -euo pipefail
LOCKFILE="/var/lock/daily_backup.lock"
exec 9>"$LOCKFILE"
if ! flock -n 9; then
echo "Error: Another instance of this backup job is already running." >&2
exit 1
fi
echo "Starting daily maintenance backup at $(date -u)"
tar -czf /backups/data-$(date +%F).tar.gz /var/www/html
echo "Backup completed successfully at $(date -u)" › Setup notes
Save this script to /usr/local/bin/backup.sh, make it executable with 'chmod +x', and then add '30 2 * * * /usr/local/bin/backup.sh' to your crontab using 'crontab -e'.
const cron = require('node-cron');
// Schedule the daily task at 2:30 AM
cron.schedule('30 2 * * *', () => {
console.log(`[${new Date().toISOString()}] Initiating scheduled daily maintenance...`);
try {
cleanupTemporaryFiles();
} catch (error) {
console.error(`[${new Date().toISOString()}] Maintenance failed:`, error);
}
}, {
scheduled: true,
timezone: "UTC"
});
function cleanupTemporaryFiles() {
console.log("Cleaning up stale session data from Redis...");
} › Setup notes
Install the dependency using 'npm install node-cron' and run this script as a long-running daemon process using PM2 or Docker.
import logging
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def daily_job():
logger.info("Executing daily sync job started at %s", datetime.utcnow())
try:
# Core sync logic goes here
pass
except Exception as e:
logger.error("Daily sync job failed: %s", str(e))
if __name__ == "__main__":
scheduler = BlockingScheduler()
trigger = CronTrigger(minute=30, hour=2, timezone='UTC')
scheduler.add_job(daily_job, trigger=trigger, id='daily_sync_job')
logger.info("Scheduler started. Job registered for 02:30 UTC.")
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
pass › Setup notes
Install APScheduler using 'pip install apscheduler' and run this script within your background worker container.
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() {
utc, err := time.LoadLocation("UTC")
if err != nil {
log.Fatalf("Failed to load timezone: %v", err)
}
c := cron.New(cron.WithLocation(utc))
_, err = c.AddFunc("30 2 * * *", func() {
log.Println("Starting daily database index optimization...")
if err := optimizeIndexes(); err != nil {
log.Printf("Error optimizing indexes: %v", err)
}
})
if err != nil {
log.Fatalf("Failed to schedule cron job: %v", err)
}
c.Start()
select {}
}
func optimizeIndexes() error {
return nil
} › Setup notes
Initialize your module, fetch the package with 'go get github.com/robfig/cron/v3', and compile the binary for deployment.
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.TimeZone;
public class DailyMaintenanceScheduler {
public static void main(String[] args) throws SchedulerException {
JobDetail job = JobBuilder.newJob(MaintenanceJob.class)
.withIdentity("dailyMaintenanceJob", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dailyMaintenanceTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 30 2 * * ?")
.inTimeZone(TimeZone.getTimeZone("UTC")))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
public static class MaintenanceJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Running scheduled maintenance task at " + System.currentTimeMillis());
}
}
} › Setup notes
Include the Quartz Scheduler dependency in your pom.xml or build.gradle, then compile and run this class as an entrypoint.
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-db-cleanup
namespace: production
spec:
schedule: "30 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
template:
spec:
containers:
- name: db-cleaner
image: postgres:15-alpine
command:
- /bin/sh
- -c
- "vacuumdb -U postgres -d main_db --analyze"
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
restartPolicy: OnFailure › Setup notes
Apply this configuration file using 'kubectl apply -f cronjob.yaml' in your target cluster. Ensure the secret 'db-credentials' exists.
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(30 2 * * ? *) Systemd Timer
OnCalendar*-*-* 02:30:00
[Unit]
Description=Timer for cron expression: 30 2 * * *
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
Last verified:
Frequently Asked Questions
How does this cron schedule behave during Daylight Saving Time (DST) transitions?
During the spring-forward transition, 2:30 AM is skipped entirely, meaning your job will not run that night on local timezones. During fall-back, it may execute twice. To prevent this, configure your server and cron daemon to use UTC.
What happens if a previous day's execution is still running at 2:30 AM?
By default, cron will start a new process, leading to overlapping executions. This can exhaust resources or cause database race conditions. Use locking mechanisms like 'flock' in Bash or distributed locks in application code to prevent concurrent runs.
Is 2:30 AM a safe time to schedule heavy database cleanup tasks?
Generally yes, as it falls in the middle of typical off-peak hours (midnight to 4 AM). However, you must verify your specific application traffic patterns and ensure it doesn't conflict with other scheduled tasks like cloud snapshots or ETL pipelines.
How can I monitor that this daily job actually ran successfully?
Since it runs only once a day, passive monitoring is insufficient. Implement 'dead-man's switch' monitoring (e.g., sending an HTTP ping to an external service at the end of the script) which alerts you if no ping is received by 2:45 AM.
Can I restrict this 2:30 AM job to run only on weekdays?
Yes. To run the job at 2:30 AM only from Monday to Friday, you can modify the expression to '30 2 * * 1-5'. This shifts the execution away from weekends when different maintenance cycles might be active.
* Explore
Related expressions you might need
Last verified: