Cron Jobs Explained: How to Schedule Tasks Like a Pro
A cron job is a scheduled task that runs automatically on Unix-like operating systems. The cron daemon (crond) reads configuration files called crontabs and executes commands at the times specified by cron expressions. Cron is the backbone of server automation — it handles log rotation, backup scripts, email reports, cache clearing, and thousands of other recurring maintenance tasks without human intervention.
Crontab Syntax
A crontab entry consists of five time-and-date fields followed by the command to execute:
* * * * * /path/to/command
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, 0 and 7 = Sunday)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
Each field accepts:
- Wildcard
*— every valid value - Specific value — e.g.,
5means "at minute 5" - Range — e.g.,
9-17means "every hour from 9 to 17" - Step — e.g.,
*/10means "every 10 units" - List — e.g.,
1,3,5means "at 1, 3, and 5" - Combination — e.g.,
9-17/2means "every 2 hours from 9 to 17"
Common Cron Schedule Examples
* * * * * → Every minute
*/5 * * * * → Every 5 minutes
0 * * * * → Every hour (at minute 0)
0 9 * * * → Daily at 9:00 AM
0 9 * * 1-5 → Weekdays at 9:00 AM
0 9 * * 1 → Every Monday at 9:00 AM
0 0 1 * * → First day of every month at midnight
0 */6 * * * → Every 6 hours
30 8 * * 1,3,5 → Monday, Wednesday, Friday at 8:30 AM
0 0 * * 0 → Every Sunday at midnight
*/15 9-17 * * * → Every 15 minutes during business hours
0 0 1 1 * → Once a year (January 1st at midnight)
Shorthand Aliases
Some systems support named schedules:
@reboot → Run once at system startup
@yearly → 0 0 1 1 *
@monthly → 0 0 1 * *
@weekly → 0 0 * * 0
@daily → 0 0 * * *
@hourly → 0 * * * *
Managing Crontabs
# Edit your user's crontab
crontab -e
# List your crontab entries
crontab -l
# Remove your crontab
crontab -r
# Edit another user's crontab (root)
sudo crontab -u www-data -e
# System-wide crontab
/etc/crontab
# Drop-in directories
/etc/cron.d/ # Individual cron files
/etc/cron.hourly/ # Scripts run hourly
/etc/cron.daily/ # Scripts run daily
/etc/cron.weekly/ # Scripts run weekly
/etc/cron.monthly/ # Scripts run monthly
Real-World Cron Examples
Backup a Database Daily
# Create a backup script
cat > /usr/local/bin/backup-db.sh << 'SCRIPT'
#!/bin/bash
BACKUP_DIR="/var/backups/mysql"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mysqldump -u root my_database | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"
find "$BACKUP_DIR" -type f -mtime +30 -delete
SCRIPT
chmod 755 /usr/local/bin/backup-db.sh
# Schedule it — runs daily at 2:00 AM
# crontab entry:
0 2 * * * /usr/local/bin/backup-db.sh
Rotate Application Logs
# Archive and compress logs older than 7 days
0 3 * * 0 find /var/log/myapp -name "*.log" -mtime +7 -exec gzip {} \;
Check Disk Space and Send Alert
# Check disk usage every hour during business hours
0 9-18 * * 1-5 df -h / | awk 'NR==2 {if ($5+0 > 80) print "Disk space critical: " $5}' | mail -s "Disk Alert" admin@example.com
Clear Cache Every 6 Hours
# Clear application cache
0 */6 * * * curl -s https://example.com/clear-cache > /dev/null 2>&1
Logging and Output Handling
By default, cron sends any output (stdout and stderr) to the user's local mail. On most servers, local mail is not configured, which causes undelivered messages to accumulate or be silently dropped.
# Redirect stdout to a log file
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Discard all output (silent cron)
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
# Separate stdout and stderr
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>> /var/log/backup.error.log
Always redirect output in production cron jobs. Unredirected output fills the mail spool and wastes disk space.
Environment Variables in Crontab
Cron runs with a minimal environment — typically PATH=/usr/bin:/bin, HOME set to the user's home directory, and no shell profile is sourced. This is the source of many cron bugs.
# Set environment variables in crontab
MAILTO=admin@example.com
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 2 * * * /usr/local/bin/backup.sh
If your script depends on environment variables defined in .bashrc or .profile, source them explicitly:
0 2 * * * source $HOME/.bashrc && /usr/local/bin/backup.sh
Better yet, define all required variables inside the script itself:
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin"
export NODE_ENV="production"
# ... script logic ...
Cron Best Practices
- Use absolute paths — Never rely on relative paths in cron commands
- Always redirect output — Append
>> /var/log/job.log 2>&1to every cron entry - Set PATH explicitly — Either in crontab or inside the script
- Test the command manually — Run the exact command in a shell before adding it to crontab
- Use lock files for long-running jobs — Prevent overlapping executions
- Add monitoring — Check that critical cron jobs ran successfully
- Use scripts, not inline commands — Put complex logic in a script file and reference it from crontab
Lock File Pattern
Prevent a cron job from running if the previous instance is still executing:
#!/bin/bash
LOCKFILE="/tmp/backup.lock"
if [ -f "$LOCKFILE" ]; then
echo "Previous instance still running, exiting"
exit 1
fi
trap "rm -f $LOCKFILE" EXIT
touch "$LOCKFILE"
# Job logic here
Common Pitfalls
Pitfall 1: Percent signs need escaping
In crontab, % has special meaning (it indicates a newline in the mail body). Escape it with a backslash:
# ❌ Wrong
0 2 * * * date +%Y%m%d
# ✅ Correct
0 2 * * * date +\%Y\%m\%d
# Better — put the date command in a script
0 2 * * * /usr/local/bin/daily-report.sh
Pitfall 2: Cron does not source your shell profile
# ❌ Wrong — assumes NVM or NODE_PATH is set
0 2 * * * node /home/user/app/script.js
# ✅ Correct — use absolute path and explicit env
0 2 * * * export PATH="/usr/local/bin:/usr/bin:/home/user/.nvm/versions/node/v18/bin:$PATH" && node /home/user/app/script.js
Pitfall 3: Overlapping jobs
If a job runs longer than its interval, two instances run simultaneously. Use a lock file or flock:
# Use flock to prevent overlap
*/5 * * * * /usr/bin/flock -n /tmp/myjob.lock /usr/local/bin/myjob.sh
Online Tools
The Cron Job Command Generator tool on Help2Code provides an interactive form with preset schedules (every minute, hourly, daily, weekly, monthly) and custom cron field inputs. Configure the command, output handling, and copy the generated crontab line. The Cron Expression Parser tool helps you decode existing cron expressions and understand when they will execute.
Conclusion
Cron is one of the most essential tools for server administration and automation. Master the five-field syntax, always handle output explicitly, set environment variables, and test commands before scheduling. Use the Cron Job Command Generator to build crontab entries quickly and the Cron Expression Parser to verify existing schedules.