What Is cron?
cron is a system daemon in Linux/Unix that schedules and executes recurring tasks. It automatically handles periodic tasks like backups, log cleanup, report generation, and data synchronization without manual intervention.
Here’s a summary of cron-related terminology.
| Term | Description |
|---|---|
| cron daemon (crond) | Scheduler process running in the background |
| crontab | Configuration file defining the list of cron jobs |
| cron job | An individual task registered in crontab |
| cron expression | 5-field expression specifying execution time |
Cron Expression Syntax
A cron expression specifies execution time using 5 fields.
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, both 0 and 7 are Sunday)
│ │ │ │ │
* * * * * command to execute
Commonly used patterns:
# Run every minute
* * * * * /opt/scripts/healthcheck.sh
# Run at the top of every hour
0 * * * * /opt/scripts/hourly-report.sh
# Run daily at 3:00 AM
0 3 * * * /opt/scripts/daily-backup.sh
# Run every Monday at 9:00 AM
0 9 * * 1 /opt/scripts/weekly-report.sh
# Run at midnight on the 1st of every month
0 0 1 * * /opt/scripts/monthly-cleanup.sh
# Run at 8:00 AM on weekdays (Mon-Fri)
0 8 * * 1-5 /opt/scripts/workday-task.sh
# Run every 10 minutes
*/10 * * * * /opt/scripts/check-service.sh
# Run daily at 9:00 AM and 6:00 PM
0 9,18 * * * /opt/scripts/sync-data.sh
# Run daily at 2:00 AM from January to March
0 2 * 1-3 * /opt/scripts/quarterly-job.sh
Special character meanings:
| Character | Meaning | Example |
|---|---|---|
* | Every value | * * * * * (every minute) |
, | Multiple values | 0,30 * * * * (at 0 and 30 minutes) |
- | Range | 1-5 (Mon-Fri) |
/ | Interval | */15 (every 15) |
crontab Commands
Commands for editing and managing crontab files.
# Edit current user's crontab
crontab -e
# List current user's crontab
crontab -l
# 0 3 * * * /opt/scripts/daily-backup.sh
# */10 * * * * /opt/scripts/healthcheck.sh
# Check specific user's crontab (requires root)
sudo crontab -u deploy -l
# Delete entire crontab (caution!)
crontab -r
# Confirm before deleting
crontab -ri
How to set environment variables in crontab:
# Environment variable settings in crontab (edit via crontab -e)
# cron runs in a minimal environment, so PATH must be specified
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
# Load environment variables file before execution
0 3 * * * source /opt/my-app/.env && /opt/scripts/backup.sh
# Or use absolute paths explicitly
0 3 * * * /usr/bin/env bash -c 'source /opt/my-app/.env && /opt/scripts/backup.sh'
Setting MAILTO sends the cron job’s output (stdout/stderr) via email. If email is unnecessary, redirect the output.
Checking Logs
How to verify whether cron jobs ran and check for errors.
# cron daemon log (Ubuntu/Debian)
sudo grep CRON /var/log/syslog
# Mar 5 03:00:01 server CRON[1234]: (deploy) CMD (/opt/scripts/daily-backup.sh)
# cron daemon log (CentOS/RHEL)
sudo grep CRON /var/log/cron
# Check only recent cron execution records
sudo grep CRON /var/log/syslog | tail -20
# Check cron execution records for a specific user
sudo grep "CRON.*deploy" /var/log/syslog
Patterns for saving cron job output to log files.
# Record both stdout and stderr to a log file
0 3 * * * /opt/scripts/backup.sh >> /var/log/my-backup.log 2>&1
# Create date-based log files
0 3 * * * /opt/scripts/backup.sh >> /var/log/backup-$(date +\%Y\%m\%d).log 2>&1
# Completely suppress output (not recommended — cannot trace errors)
0 3 * * * /opt/scripts/backup.sh > /dev/null 2>&1
In crontab, % is interpreted as a newline. You must escape it as \% when using % within commands.
Practical Automation Examples
Database Backup Script
#!/bin/bash
# /opt/scripts/backup-db.sh
# PostgreSQL daily backup script
# Environment variables
BACKUP_DIR="/opt/backups/db"
DB_NAME="myapp_production"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
# Create backup directory
mkdir -p "${BACKUP_DIR}"
# Backup with pg_dump (gzip compressed)
pg_dump "${DB_NAME}" | gzip > "${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz"
# Check if backup succeeded
if [ $? -eq 0 ]; then
echo "[$(date)] Backup successful: ${DB_NAME}_${DATE}.sql.gz"
# Delete old backups (older than 7 days)
find "${BACKUP_DIR}" -name "*.sql.gz" -mtime +${RETENTION_DAYS} -delete
echo "[$(date)] Cleaned up backups older than ${RETENTION_DAYS} days"
else
echo "[$(date)] Backup failed: ${DB_NAME}" >&2
exit 1
fi
Log Cleanup Script
#!/bin/bash
# /opt/scripts/cleanup-logs.sh
# Script to clean up old log files
LOG_DIRS=(
"/var/log/my-app"
"/var/log/nginx"
"/opt/my-app/logs"
)
RETENTION_DAYS=30
for dir in "${LOG_DIRS[@]}"; do
if [ -d "${dir}" ]; then
# Delete log files older than specified days
deleted=$(find "${dir}" -name "*.log" -mtime +${RETENTION_DAYS} -delete -print | wc -l)
# Also delete compressed logs
deleted_gz=$(find "${dir}" -name "*.log.gz" -mtime +${RETENTION_DAYS} -delete -print | wc -l)
echo "[$(date)] ${dir}: ${deleted} logs, ${deleted_gz} compressed logs deleted"
fi
done
# Report disk usage
echo "[$(date)] Disk usage:"
df -h / | tail -1
crontab Registration
# Register via crontab -e
# Daily DB backup at 3:00 AM
0 3 * * * /opt/scripts/backup-db.sh >> /var/log/backup-db.log 2>&1
# Weekly log cleanup at 4:00 AM on Sundays
0 4 * * 0 /opt/scripts/cleanup-logs.sh >> /var/log/cleanup.log 2>&1
# Service health check every 5 minutes
*/5 * * * * curl -sf http://localhost:3000/health > /dev/null || systemctl restart my-app
Debugging cron Jobs
Things to check when cron jobs don’t work as expected.
# 1. Verify the script has execute permission
ls -l /opt/scripts/backup-db.sh
chmod +x /opt/scripts/backup-db.sh
# 2. Run the script manually to check for errors
/opt/scripts/backup-db.sh
# 3. Test in cron environment (simulate minimal environment)
env -i SHELL=/bin/bash PATH=/usr/bin:/bin HOME=/home/deploy /opt/scripts/backup-db.sh
# 4. Verify the cron daemon is running
systemctl status cron
# ● cron.service - Regular background program processing daemon
# Active: active (running)
# 5. Validate cron expression (use crontab.guru)
# https://crontab.guru/#0_3_*_*_*
Practical Tips
- Use absolute paths: cron does not load
.bashrc/.profile, so PATH is limited. Use absolute paths (/usr/bin/python3) for all commands in scripts, or declare PATH at the top of the script. - Always redirect output: If you don’t redirect cron job output to a log file (
>> /var/log/xxx.log 2>&1), output accumulates as emails or is lost entirely. - Shorthand expressions: Shortcuts like
@reboot(on boot),@daily(midnight daily),@hourly(top of every hour),@weekly(Sunday midnight), and@monthly(1st at midnight) are also supported. - Prevent concurrent execution: For long-running tasks, use
flockto prevent duplicate runs. Example:0 * * * * flock -n /tmp/mylock.lock /opt/scripts/long-task.sh. - Compare with systemd timers: For new projects, consider systemd timers as well. They offer better log management than cron and support
Persistent=trueto catch up on missed runs. However, for simple recurring tasks, cron is easier to configure.