Quick Answer:
crontab -eopens your cron jobs. Add a line like0 2 * /opt/backup.shto run backup.sh at 2 AM daily. Format:minute hour day-of-month month day-of-week command. Use our Crontab Explainer to translate any expression.
What is Crontab?
Crontab is Linux's built-in task scheduler. It runs commands automatically at specified times — backups at midnight, log cleanup every week, health checks every 5 minutes.
Every user has their own crontab. Root has a separate one for system tasks.
Basic Commands
# Edit your cron jobs
crontab -e
# List your cron jobs
crontab -l
# Remove all your cron jobs (careful!)
crontab -r
# Edit root's crontab
sudo crontab -e
# Edit another user's crontab
sudo crontab -u www-data -e
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
| Symbol | Meaning | Example |
|---|---|---|
* |
Every | * = every minute |
*/N |
Every N | /5 * = every 5 minutes |
N |
At exactly N | 0 2 * = at 2:00 AM |
N,M |
At N and M | 0,30 = at :00 and :30 |
N-M |
Range N to M | 0 9-17 * = every hour 9 AM to 5 PM |
Common Examples
Time-Based
# Every minute
* * * * * /opt/scripts/check.sh
# Every 5 minutes
*/5 * * * * /opt/scripts/monitor.sh
# Every 15 minutes
*/15 * * * * /opt/scripts/sync.sh
# Every hour (at minute 0)
0 * * * * /opt/scripts/hourly.sh
# Every day at midnight
0 0 * * * /opt/scripts/daily.sh
# Every day at 2:30 AM
30 2 * * * /opt/scripts/backup.sh
# Every Monday at 9 AM
0 9 * * 1 /opt/scripts/weekly-report.sh
# Every weekday at 9 AM
0 9 * * 1-5 /opt/scripts/workday.sh
# First day of every month at midnight
0 0 1 * * /opt/scripts/monthly.sh
# Every 6 hours
0 */6 * * * /opt/scripts/periodic.sh
# Twice a day (8 AM and 8 PM)
0 8,20 * * * /opt/scripts/twice-daily.sh
Real-World Tasks
# Backup database every night at 2 AM
0 2 * * * mysqldump -u root mydb | gzip > /backups/db-$(date +\%Y\%m\%d).sql.gz
# Clean old log files every Sunday at 3 AM
0 3 * * 0 find /var/log -name "*.log" -mtime +30 -delete
# Renew SSL certificates (twice daily, Let's Encrypt recommends)
0 0,12 * * * certbot renew --quiet
# Check disk space, email alert if >90%
*/30 * * * * df -h / | awk 'NR==2{if($5+0>90) print}' | mail -s "Disk Alert" [email protected]
# Restart a service every night (if it has memory leaks)
0 4 * * * systemctl restart myapp
# Pull latest code and restart (auto-deploy)
*/10 * * * * cd /opt/myapp && git pull && systemctl restart myapp
# Sync files to backup server
0 1 * * * rsync -avz /data/ backup-server:/backups/data/
Logging Cron Output
By default, cron output goes to your system mail. Better options:
# Log output to a file
0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
# Discard output (silent)
0 2 * * * /opt/backup.sh > /dev/null 2>&1
# Log only errors
0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>> /var/log/backup-errors.log
The 2>&1 redirects stderr to the same place as stdout.
Important Rules
Use Full Paths
Cron runs with a minimal environment. Always use full paths:
# Bad (might not find the command)
0 2 * * * backup.sh
# Good
0 2 * * * /opt/scripts/backup.sh
# Or set PATH at the top of crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * backup.sh
Escape Percent Signs
In crontab, % has special meaning (newline). Escape it with \%:
# Bad
0 2 * * * echo "Today is $(date +%Y-%m-%d)"
# Good
0 2 * * * echo "Today is $(date +\%Y-\%m-\%d)"
Make Scripts Executable
chmod +x /opt/scripts/backup.sh
Add a Shebang
Your scripts should start with:
#!/bin/bash
System-Wide Cron
Besides user crontabs, Linux has system cron directories:
| Location | Schedule |
|---|---|
/etc/cron.d/ |
Custom schedules (like crontab format) |
/etc/cron.daily/ |
Runs once per day |
/etc/cron.weekly/ |
Runs once per week |
/etc/cron.monthly/ |
Runs once per month |
/etc/cron.hourly/ |
Runs once per hour |
Drop a script into any of these directories and it runs automatically:
# Run daily
sudo cp myscript.sh /etc/cron.daily/
sudo chmod +x /etc/cron.daily/myscript.sh
Troubleshooting
| Problem | Fix |
|---|---|
| Cron job not running | Check crontab -l — is it there? Check syntax |
| Runs but nothing happens | Add logging: >> /tmp/cron.log 2>&1 |
| Command not found | Use full paths: /usr/bin/python3 not python3 |
| Script works manually but not in cron | Environment differs — set PATH in crontab |
| Runs at wrong time | Check server timezone: timedatectl |
| Permission denied | chmod +x script.sh and check file ownership |
# Check if cron service is running
systemctl status cron
# Check cron logs
grep CRON /var/log/syslog | tail -20
# Check your timezone
timedatectl
Cron vs systemd Timers
| Cron | systemd Timer | |
|---|---|---|
| Simplicity | Simple one-liner | Requires two files |
| Logging | Manual | Built into journalctl |
| Dependencies | None | Can depend on other services |
| Missed runs | Skipped | Can catch up (Persistent=true) |
| Best for | Simple scheduled tasks | Complex service scheduling |
For most tasks, cron is fine. Use systemd timers for services that need dependency management.
Shorthand Schedules
| Shortcut | Equivalent | When |
|---|---|---|
@reboot |
-- | Once at startup |
@yearly |
0 0 1 1 * |
January 1st |
@monthly |
0 0 1 |
First of month |
@weekly |
0 0 0 |
Every Sunday |
@daily |
0 0 * |
Every midnight |
@hourly |
0 |
Every hour |
@reboot /opt/scripts/startup.sh
@daily /opt/backup.sh >> /var/log/backup.log 2>&1
Related Tools
- Crontab Explainer — translate cron expressions to English
- Cron Generator — build cron expressions visually