Quick Answer: Edit crontab:
crontab -e. Add a job:0 2 * /opt/backup.sh(runs at 2:00 AM daily). View jobs:crontab -l. Format:minute hour day month weekday command.
Cron Syntax
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * * command
| Field | Values | Special |
|---|---|---|
| Minute | 0-59 | * = every minute |
| Hour | 0-23 | * = every hour |
| Day of month | 1-31 | * = every day |
| Month | 1-12 | * = every month |
| Day of week | 0-7 | 0 and 7 = Sunday |
Special Characters
| Character | Meaning | Example |
|---|---|---|
* |
Every value | * = every minute |
, |
List | 1,15 = minute 1 and 15 |
- |
Range | 1-5 = minutes 1 through 5 |
/ |
Step | /10 * = every 10 minutes |
Common Schedules
| Expression | When It Runs |
|---|---|
* |
Every minute |
/5 * |
Every 5 minutes |
/15 * |
Every 15 minutes |
0 |
Every hour (at :00) |
0 /2 |
Every 2 hours |
0 0 * |
Every day at midnight |
0 2 * |
Every day at 2:00 AM |
0 9 1-5 |
Weekdays at 9:00 AM |
0 0 0 |
Every Sunday at midnight |
0 0 1 |
First of every month at midnight |
0 0 1 1 * |
January 1st at midnight (yearly) |
30 4 1 |
Every Monday at 4:30 AM |
0 9,17 * |
9:00 AM and 5:00 PM daily |
Shortcut Strings
| Shortcut | Equivalent | When |
|---|---|---|
@reboot |
— | Once at startup |
@yearly |
0 0 1 1 * |
Once a year |
@monthly |
0 0 1 |
Once a month |
@weekly |
0 0 0 |
Once a week |
@daily |
0 0 * |
Once a day |
@hourly |
0 |
Once an hour |
Managing Crontab
# Edit your crontab
crontab -e
# View your crontab
crontab -l
# Remove your crontab (deletes ALL jobs)
crontab -r
# Edit another user's crontab
sudo crontab -u sam -e
# View another user's crontab
sudo crontab -u sam -l
First time? crontab -e will ask which editor to use. Pick nano for simplicity.
Practical Examples
Backups
# Daily database backup at 2 AM
0 2 * * * docker exec postgres pg_dumpall -U postgres | gzip > /backups/db-$(date +\%Y\%m\%d).sql.gz
# Weekly full backup on Sunday at 3 AM
0 3 * * 0 tar czf /backups/weekly-$(date +\%Y\%m\%d).tar.gz /var/www/
# Delete backups older than 30 days (runs daily at 4 AM)
0 4 * * * find /backups -name "*.gz" -mtime +30 -delete
Monitoring
# Check if website is up every 5 minutes
*/5 * * * * curl -sf https://samnet.dev > /dev/null || echo "Site down!" | mail -s "Alert" [email protected]
# Log disk usage hourly
0 * * * * df -h >> /var/log/disk-usage.log
# Restart service if it crashes (check every minute)
* * * * * systemctl is-active --quiet nginx || systemctl restart nginx
Maintenance
# Update system packages weekly (Sunday 4 AM)
0 4 * * 0 apt update && apt upgrade -y >> /var/log/apt-update.log 2>&1
# Clear temp files daily at midnight
0 0 * * * find /tmp -type f -mtime +7 -delete
# Renew SSL certificates (twice daily, certbot handles timing)
0 0,12 * * * certbot renew --quiet
# Rotate logs weekly
0 0 * * 0 logrotate /etc/logrotate.conf
Scripts
# Run a Python script every hour
0 * * * * /usr/bin/python3 /opt/scripts/report.py >> /var/log/report.log 2>&1
# Run a Bash script at startup
@reboot /opt/scripts/startup.sh
# Run Node.js script every 30 minutes
*/30 * * * * cd /opt/myapp && /usr/bin/node index.js >> /var/log/myapp.log 2>&1
Important Tips
Use Full Paths
Cron runs with a minimal environment. Always use full paths:
# Wrong (may not find the command)
0 * * * * python3 script.py
# Correct (full paths)
0 * * * * /usr/bin/python3 /opt/scripts/script.py
Find the full path: which python3
Escape Percent Signs
In crontab, % has special meaning (newline). Escape it with \:
# Wrong
0 2 * * * echo $(date +%Y%m%d)
# Correct
0 2 * * * echo $(date +\%Y\%m\%d)
Redirect Output
By default, cron emails output. Redirect to a file or discard:
# Log output to file
0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
# Discard all output
0 2 * * * /opt/backup.sh > /dev/null 2>&1
# Log errors only
0 2 * * * /opt/backup.sh > /dev/null
Set Environment Variables
# Add at the top of crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
[email protected]
# Then your jobs
0 2 * * * /opt/backup.sh
System Cron Files
Besides per-user crontabs, there are system-wide cron directories:
# System crontab (has an extra "user" field)
/etc/crontab
# Drop-in directories (just place a script here)
/etc/cron.hourly/
/etc/cron.daily/
/etc/cron.weekly/
/etc/cron.monthly/
# Example: daily cleanup script
sudo nano /etc/cron.daily/cleanup
# Add script content, then:
sudo chmod +x /etc/cron.daily/cleanup
Troubleshooting
# Check if cron is running
systemctl status cron
# or
systemctl status crond # CentOS/RHEL
# View cron logs
grep CRON /var/log/syslog
# or
journalctl -u cron
# Test your command manually first
/opt/scripts/backup.sh
# If it works manually but not in cron, it's usually a PATH or permissions issue
| Problem | Fix |
|---|---|
| Job doesn't run | Check systemctl status cron. Check syntax with crontab validator |
| Command not found | Use full paths (/usr/bin/python3 not python3) |
| Permission denied | Make script executable: chmod +x script.sh |
% in command breaks |
Escape as \% |
| No output/logging | Add >> /var/log/job.log 2>&1 |
| Wrong timezone | Check timedatectl. Cron uses system timezone |
| Runs but does nothing | Environment differs from shell. Set PATH in crontab |
| Email flooding | Add > /dev/null 2>&1 or set MAILTO="" |
Related Guides
- Crontab Tutorial — detailed crontab guide
- Cron Examples — real-world cron jobs
- Bash Cheat Sheet — scripting for cron jobs
- Systemd Cheat Sheet — systemd timers (alternative)
- Server Hardening Guide — automated security
Related Tools
- Cron Generator — build cron expressions visually
- Crontab Explainer — explain cron schedules in English