You know that feeling when you realize you've been doing the same boring task every single day — and it could have been automated the whole time?
That's what cron is for.
Cron is Linux's built-in task scheduler. It runs commands, scripts, or programs automatically at specific times — daily, hourly, every Monday, the 1st of each month, whatever you need. Once you set it up, you forget about it and it just works.
If you've followed our Linux Terminal Basics and SSH Beginner's Guide, cron is the natural next step — it turns you from someone who uses Linux into someone who automates Linux.
What Is Cron, Really?
Think of cron as a robot alarm clock. You tell it: "wake up at 3 AM every day and run this backup script." It does exactly that — no complaints, no forgetting, no "I was too busy."
It's been part of Unix-like systems since the 1970s and is still the standard way to schedule tasks on Linux servers, Raspberry Pis, and even macOS.
The name "cron" comes from the Greek word chronos (time). The program that runs in the background checking the schedule is called cron (the daemon), and each scheduled task is called a cron job.
Your First Cron Job in 60 Seconds
Open your terminal and type:
crontab -e
If this is your first time, it'll ask which editor to use. Pick nano (usually option 1) — it's the easiest for beginners.
Add this line at the bottom:
*/5 * * * * echo "I'm learning cron!" >> ~/cron-test.log
Save and exit (Ctrl+O, Enter, Ctrl+X in nano). That's it — you just created your first cron job. Every 5 minutes, it appends "I'm learning cron!" to a log file in your home directory.
Wait 5 minutes, then check:
cat ~/cron-test.log
# Output:
# I'm learning cron!
# I'm learning cron!
It ran. Congratulations — you've automated something.
Understanding Cron Syntax
That */5 * * * * looks like alien code at first glance. Here's what each position means:
* * * * * command_to_run
│ │ │ │ │
│ │ │ │ └── Day of week (0-7, 0 and 7 = Sunday)
│ │ │ └────── Month (1-12)
│ │ └─────────── Day of month (1-31)
│ └──────────────── Hour (0-23)
└───────────────────── Minute (0-59)
A * means "every." So * * * * * means "every minute of every hour of every day..." — basically, constantly.
Here's a cheat sheet for the most common schedules:
| Schedule | Cron Syntax | Meaning |
|---|---|---|
| Every minute | * * * * * |
Runs 1,440 times per day — rarely useful! |
| Every 15 minutes | */15 * * * * |
Good for health checks |
| Every hour | 0 * * * * |
At the top of every hour |
| Daily at 3 AM | 0 3 * * * |
Classic backup time |
| Daily at 9 AM | 0 9 * * * |
Morning report or email |
| Weekdays at 9 AM | 0 9 * * 1-5 |
Monday through Friday |
| Every Sunday at 3 AM | 0 3 * * 0 |
Weekly cleanup |
| 1st of month at midnight | 0 0 1 * * |
Monthly billing or reports |
| At reboot | @reboot |
Run once when the system starts |
How the Fields Work Together
A few more examples to solidify the concept:
# Run at 8:30 AM every day
30 8 * * * /home/user/morning-report.sh
# Run at 11:45 PM every Saturday
45 23 * * 6 /home/user/weekend-cleanup.sh
# Run every 2 hours (at :00 of even hours)
0 */2 * * * /usr/local/bin/health-check.sh
# Run on Jan 1st at midnight (annual report)
0 0 1 1 * /opt/scripts/yearly-summary.sh
The key insight: minute and hour control the time, the other three control the date.
Managing Your Cron Jobs
Once you start creating cron jobs, you'll need to manage them. Here are the essential commands:
crontab -e # Edit your cron jobs
crontab -l # List all your cron jobs
crontab -r # Remove ALL your cron jobs (careful!)
To edit someone else's cron jobs (as root):
crontab -u username -e # Edit another user's crontab
Each user on the system has their own crontab. Your jobs run with your permissions — so a backup script that runs as your user can only access files you own, unless you use sudo inside the script.
Real-World Cron Job Examples
1. Automated Database Backup (Daily at 3 AM)
0 3 * * * /home/user/scripts/backup-db.sh >> /var/log/backup.log 2>&1
The >> /var/log/backup.log 2>&1 part is important — it saves both normal output AND error messages to a log file. Without this, you'd never know if the job failed.
Here's what a simple backup script might look like:
#!/bin/bash
# backup-db.sh — dump a MySQL database and keep the last 7 copies
DB_NAME="myapp"
BACKUP_DIR="/backups/db"
DATE=$(date +%Y-%m-%d)
mkdir -p "$BACKUP_DIR"
mysqldump "$DB_NAME" > "$BACKUP_DIR/$DB_NAME-$DATE.sql"
# Delete backups older than 7 days
find "$BACKUP_DIR" -name "*.sql" -mtime +7 -delete
echo "[$(date)] Backup complete: $DB_NAME-$DATE.sql"
2. Disk Space Monitor (Every Hour)
0 * * * * /home/user/scripts/check-disk.sh
#!/bin/bash
# check-disk.sh — send an alert if disk usage exceeds 90%
USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$USAGE" -gt 90 ]; then
echo "WARNING: Disk usage at ${USAGE}% on $(hostname)" | \
mail -s "Disk Alert" admin@example.com
fi
This is a classic cron use case — monitor something, alert when something's wrong. Cron doesn't just run tasks; it can be your early warning system.
3. Update Package Lists (Weekly)
0 4 * * 0 sudo apt update && sudo apt upgrade -y >> /var/log/apt-updates.log
Run this on a Sunday at 4 AM, and your server keeps itself updated. The -y flag auto-confirms — use it cautiously in production!
4. Clean Up Temporary Files (Daily at Midnight)
0 0 * * * find /tmp -type f -mtime +7 -delete 2>/dev/null
This deletes files in /tmp that are older than 7 days. Simple, silent, effective — the perfect cron job.
Special Shortcut Notations
Instead of writing five-field schedules, cron supports these human-readable shortcuts:
| Shortcut | Equivalent To | Use Case |
|---|---|---|
@reboot |
(runs once at boot) | Start services, mount drives |
@yearly |
0 0 1 1 * |
Annual reporting |
@monthly |
0 0 1 * * |
Billing, archiving |
@weekly |
0 0 * * 0 |
Cleanups, updates |
@daily |
0 0 * * * |
Backups, log rotation |
@hourly |
0 * * * * |
Health checks |
Example:
@reboot /home/user/scripts/start-my-app.sh
@daily /home/user/scripts/backup.sh
These are easier to read but less flexible — you can't say "daily at 5 AM" with @daily (it runs at midnight). Use the full five-field syntax when you need precise control.
System-Wide Cron vs. User Cron
So far we've used crontab -e, which edits your user's cron jobs. But Linux also has system-wide cron directories:
ls /etc/cron.*
# /etc/cron.d/ — custom cron files (with a username field)
# /etc/cron.daily/ — scripts here run once per day (via anacron)
# /etc/cron.hourly/ — scripts here run once per hour
# /etc/cron.weekly/ — scripts here run once per week
# /etc/cron.monthly/ — scripts here run once per month
These are managed by the system, not individual users. You can drop a script into /etc/cron.daily/ (make it executable with chmod +x), and it'll run automatically as root.
When to use which:
- User crontab (
crontab -e): Your personal projects, scripts in your home directory - System directories (
/etc/cron.*): Server-wide tasks that need root privileges
Debugging Cron Jobs: The 3 Most Common Problems
Cron can be frustrating when things don't work. Here's what goes wrong 90% of the time:
Problem 1: PATH Is Different
Cron runs with a minimal PATH. Commands that work in your terminal might not be found by cron.
Fix: Always use absolute paths in cron jobs:
# ❌ BAD — python might not be found
0 3 * * * python /home/user/backup.py
# ✅ GOOD — full path to the exact Python you want
0 3 * * * /usr/bin/python3 /home/user/backup.py
Find the full path of any command with which:
which python3
# /usr/bin/python3
Problem 2: No Output, No Clue
If a cron job fails silently, you'll never know.
Fix: Always log output:
0 3 * * * /home/user/backup.sh >> /var/log/backup.log 2>&1
The >> appends stdout, and 2>&1 redirects stderr to the same place. Now errors won't be silent.
Problem 3: Script Permissions
Your script runs fine manually but fails in cron — probably a permissions issue.
Fix: Make sure the script is executable:
chmod +x /home/user/backup.sh
And test it the same way cron runs it:
env -i HOME=$HOME /bin/bash -c "/home/user/backup.sh"
The env -i strips the environment (like cron does), revealing any hidden dependencies.
Cron + SSH: The Power Combo
Once you've mastered SSH (covered in our SSH Beginner's Guide), you unlock a powerful pattern: cron jobs that act on remote servers.
# Daily: pull remote database backup to local machine
0 4 * * * scp user@remote-server:/backups/latest.sql /local/backups/
Or the reverse — run a command on a remote server from your local cron:
# Check if remote web server is responding
*/5 * * * * ssh monitor@web-server "curl -s -o /dev/null -w '%{http_code}' http://localhost" >> /var/log/health.log
This is how small teams set up monitoring without paying for SaaS tools — just cron, SSH, and a few lines of bash.
Best Practices Cheat Sheet
| Practice | Why |
|---|---|
| Use absolute paths | Avoids PATH issues |
Always log output (>> file 2>&1) |
Cron emails are unreliable on modern systems |
Add comments (# what this does) |
You won't remember in 6 months |
| Don't overlap jobs | Use a lock file if a job might run longer than its interval |
Test with */5 first |
Verify it works at high frequency, then switch to the real schedule |
Use MAILTO="" to suppress email |
Add MAILTO="" at the top of crontab if cron keeps emailing root |
| Non-zero exit = cron sends mail | Return 0 on success, non-zero signals failure |
Here's a well-structured crontab example that follows all best practices:
# ── My Cron Jobs ──────────────────────────────────────
MAILTO=""
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
# Daily database backup at 3:00 AM
0 3 * * * /home/user/scripts/backup-db.sh >> /var/log/backup.log 2>&1
# Check disk space every hour
0 * * * * /home/user/scripts/check-disk.sh >> /var/log/disk-check.log 2>&1
# Clean up temp files every Sunday at 2 AM
0 2 * * 0 /home/user/scripts/cleanup.sh >> /var/log/cleanup.log 2>&1
Quick Reference: Build Your Own Cron Schedule
Use this table to construct any schedule. Find your desired frequency, copy the five-field expression:
| I want it to run... | Minute | Hour | Day | Month | Weekday |
|---|---|---|---|---|---|
| Every 5 minutes | */5 |
* |
* |
* |
* |
| Every 30 minutes | */30 |
* |
* |
* |
* |
| Every hour at :15 | 15 |
* |
* |
* |
* |
| Twice a day (9am, 5pm) | 0 |
9,17 |
* |
* |
* |
| Daily at 3 AM | 0 |
3 |
* |
* |
* |
| Weekdays at 8 AM | 0 |
8 |
* |
* |
1-5 |
| Weekends at midnight | 0 |
0 |
* |
* |
0,6 |
| Every Monday at 6 AM | 0 |
6 |
* |
* |
1 |
| 1st and 15th at noon | 0 |
12 |
1,15 |
* |
* |
| Once a year (Jan 1) | 0 |
0 |
1 |
1 |
* |
What's Next?
Cron is the foundation of Linux automation. Once you're comfortable scheduling tasks, the next natural step is automating with Python — where you can write richer, more intelligent scripts than bash alone.
Check out:
- Automate Your Weekly Backups with Python — a cron-powered backup system in Python
- Automate File Renaming with Python — save hours of manual file organization
- Linux File Permissions: chmod for Beginners — file permissions (critical for making scripts executable!)
Set up your first cron job today. Pick something small — a daily log, a disk space check, a "good morning" file. The habit of automating repetitive work is more valuable than any single cron job.