Scheduling Tasks with cron

Reviewed & published by Brayan K

By the end of this lesson you'll be able to schedule commands to run automatically — every night, every 15 minutes, or once at startup — using cron's five schedule fields, special shortcuts, and a crontab that actually runs reliably.

Part of the free Command Line course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

1. The crontab: -e, -l and -r

Your personal schedule lives in a file called your crontab, but you never open that file by hand — you manage it with three commands. crontab -e edits it (this is how you add jobs), crontab -l lists it, and crontab -r removes it. Be careful: crontab -r deletes your entire crontab with no confirmation, and it's one key away from -e — a classic footgun. Back up first with crontab -l > backup.txt.

# cron is a time-based job scheduler on Unix/Linux.
# Your personal schedule lives in a file called your "crontab".
# You never edit that file directly — you use these three commands:

crontab -e     # EDIT your crontab (opens it in an editor)
crontab -l     # LIST (print) your crontab to the screen
crontab -r     # REMOVE your crontab — DELETES THE WHOLE THING

# WARNING: crontab -r wipes your entire crontab with no confirmation.
# It sits one key away from crontab -e, so it is a classic footgun.
# Make a backup first:   crontab -l > my-cron-backup.txt

2. The Five Schedule Fields

Every cron line is five time fields followed by the command. The order, left to right, is always: minute (0-59), hour (0-23), day-of-month (1-31), month (1-12), and day-of-week (0-7, where both 0 and 7 mean Sunday). So 30 2 * * * means minute 30, hour 2, any day, any month, any weekday — i.e. 02:30 every day.

# Every cron line has 5 time fields, then the command to run.
# Read them LEFT to RIGHT, in THIS exact order:
#
#   * * * * *  command_to_run
#   | | | | |
#   | | | | +--- day-of-week  (0-7)   0 and 7 BOTH mean Sunday
#   | | | +----- month        (1-12)
#   | | +------- day-of-month (1-31)
#   | +--------- hour         (0-23)  0 = midnight, 23 = 11 PM
#   +----------- minute       (0-59)
#
# A literal example — run a backup at 02:30 every single day:

30 2 * * *  /home/user/scripts/backup.sh

# minute=30, hour=2, day-of-month=any, month=any, day-of-week=any

3. Field Syntax: *, Steps, Ranges & Lists

Each field accepts four building blocks. * means every value. */n is a step — */5 in the minute field means every 5 minutes. a-b is a range — 1-5 in day-of-week is Monday through Friday. And a,b,c is a list — 1,15,30. Combine them to express almost any schedule you need.

# The four building blocks you can put in ANY field:
#
#   *      every value           (every minute / every hour / ...)
#   */n    a step: every n       (*/5 in minutes = every 5 minutes)
#   a-b    a range               (1-5 = Monday through Friday)
#   a,b,c  a list                (1,15,30 = minutes 1, 15 and 30)
#
# Worked examples (each line = schedule + what it means):

0 * * * *      /path/cmd     # top of EVERY hour (minute 0)
*/15 * * * *   /path/cmd     # every 15 minutes
0 9 * * 1-5    /path/cmd     # 09:00, Monday through Friday
0 0 1 * *      /path/cmd     # midnight on the 1st of each month
30 8 1,15 * *  /path/cmd     # 08:30 on the 1st and the 15th

4. Special Shortcuts: @daily, @reboot & Friends

For common schedules, cron offers shortcuts that replace all five fields. @reboot runs a job once when the system boots; @hourly, @daily, @weekly, @monthly, and @yearly are readable aliases — for example @daily is exactly 0 0 * * *. You still add the command after the shortcut.

# Instead of five fields, cron accepts these handy shortcuts.
# They replace the WHOLE schedule (you still add a command after).

@reboot     /path/startup.sh   # run ONCE, each time the system boots
@hourly     /path/cmd          # = 0 * * * *   (top of every hour)
@daily      /path/cmd          # = 0 0 * * *   (every day at midnight)
@weekly     /path/cmd          # = 0 0 * * 0   (Sunday at midnight)
@monthly    /path/cmd          # = 0 0 1 * *   (1st of month, midnight)
@yearly     /path/cmd          # = 0 0 1 1 *   (Jan 1st at midnight)

# @midnight is also accepted as an alias for @daily on most systems.

5. Output, Email & Logging

By default, cron emails whatever a job prints (stdout and stderr) to the local user. Put MAILTO="you@example.com" at the top of the crontab to choose the recipient, or MAILTO="" to switch that mail off. More commonly, people send output to a log file by appending >> /var/log/task.log 2>&1 — where 2>&1 routes errors into the same log so nothing is lost.

# By default, cron EMAILS any output (stdout + stderr) a job
# produces to the local user. Two settings (put them at the TOP
# of the crontab, before the job lines) control where mail goes:

MAILTO="you@example.com"   # send job output to this address
# MAILTO=""                # set it EMPTY to disable cron mail entirely

# Most people skip email and write output to a LOG file instead.
# Append both stdout and stderr to a log with  >> file 2>&1 :

0 3 * * *  /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1

#   >>            append (don't overwrite) the log file
#   2>&1          send errors (stderr) to the same place as output

6. cron's Minimal Environment (use absolute paths!)

This is the single most common reason a cron job "works in my shell but not in cron." cron runs with a minimal environment: a sparse PATH (often just /usr/bin:/bin) and none of your ~/.bashrc or login settings. So always use absolute paths to your commands and scripts — /usr/bin/python3 /home/user/app/job.py — or set PATH= at the top of the crontab. As a fast contrast: at runs a command once at a future time, and systemd timers are a modern alternative on systemd-based Linux.

# THE #1 reason a cron job "works in my shell but not in cron":
# cron runs with a MINIMAL environment. Its PATH is sparse (often
# just /usr/bin:/bin) and it does NOT load your ~/.bashrc or profile.
#
# So a bare command name that works when YOU type it may not be found.

# RISKY — relies on PATH that cron may not have:
0 * * * *  python myscript.py

# RELIABLE — use ABSOLUTE paths to the program AND the script:
0 * * * *  /usr/bin/python3 /home/user/app/myscript.py

# Alternatively, set PATH once at the top of the crontab:
PATH=/usr/local/bin:/usr/bin:/bin
0 * * * *  python3 /home/user/app/myscript.py

🎯 Your Turn: schedule a daily 2:30 AM backup

Fill in the two blanks marked ___ using the # 👉 hints, then read the line back left to right and check it matches the Output panel.

# 🎯 YOUR TURN — schedule a backup at 2:30 AM EVERY day.
# Fill in the two blanks, then read it back left to right.

# Fields order:  minute hour day-of-month month day-of-week  command
___ ___ * * *  /home/user/scripts/backup.sh
#  |   |
#  |   👉 replace the 2nd ___ with the HOUR for 2 AM   ->  2
#  👉 replace the 1st ___ with the MINUTE (thirty)     ->  30

# ✅ Expected line:
#    30 2 * * *  /home/user/scripts/backup.sh
#    (runs at 02:30 every day)

🎯 Your Turn: run a check every 15 minutes

Two blanks again: pick the step syntax for "every 15 minutes," and the redirect that appends output to a log. Then check the Output panel.

# 🎯 YOUR TURN — run a health check EVERY 15 minutes and log it.
# Fill in the two blanks.

# 1) Step syntax for "every 15 minutes" goes in the minute field.
___ * * * *  /home/user/scripts/healthcheck.sh ___ /var/log/health.log 2>&1
#  |                                            |
#  |                                            👉 replace 2nd ___ with  >>
#  👉 replace 1st ___ with the step for every 15 min  ->  */15

# ✅ Expected line:
#    */15 * * * *  /home/user/scripts/healthcheck.sh >> /var/log/health.log 2>&1

Pro Tips

Common Errors (and the fix)

📋 Quick Reference

ConceptSyntaxNotes
Edit crontabcrontab -eAdd or change jobs
List crontabcrontab -lPrint your jobs
Remove crontabcrontab -rDeletes ALL of it
Field ordermin hr dom mon dowThen the command
Every value*Matches all
Step*/5Every 5 (e.g. minutes)
Range1-5Mon-Fri in dow
List1,15,30Specific values
Daily shortcut@daily= 0 0 * * *
At boot@rebootRuns once at startup
Email outputMAILTO="..."Empty = no mail
Log output>> file 2>&1Append out + errors

Mini-Challenge: a weekday morning report

No blanks this time — just a brief and an outline. Build the full cron line that runs a report script at 8:00 AM on weekdays and logs its output. Plan each field in order, use the absolute path, and check your line against the target in the comments.

# 🎯 MINI-CHALLENGE: a weekday morning report job
#
# Goal: schedule /home/user/scripts/report.sh to run at 8:00 AM,
# Monday through Friday, sending all output to /var/log/report.log.
#
# Build the cron line yourself. Plan each field in order:
#   1. minute        -> 0
#   2. hour          -> 8
#   3. day-of-month  -> * (any)
#   4. month         -> * (any)
#   5. day-of-week   -> the range that means Mon..Fri  (hint: 1-5)
#   6. command       -> the script, then  >> /var/log/report.log 2>&1
#
# Remember: use the ABSOLUTE path to the script, because cron has a
# minimal PATH. Add it with  crontab -e  and confirm with  crontab -l.
#
# ✅ Target line:
#    0 8 * * 1-5  /home/user/scripts/report.sh >> /var/log/report.log 2>&1

# your line here

🎉 Lesson Complete!

Practice quiz

What is the order of the 5 cron schedule fields?

  • minute hour day-of-month month day-of-week
  • minute hour month day-of-month day-of-week
  • second minute hour day month
  • hour minute day-of-month month day-of-week

Answer: minute hour day-of-month month day-of-week. The five fields, in order, are: minute, hour, day-of-month, month, day-of-week, then the command.

How do you edit your own crontab?

  • crontab -r
  • cron edit
  • crontab -e
  • crontab -l

Answer: crontab -e. crontab -e opens YOUR crontab in an editor; -l lists it and -r removes it.

What does */5 in the minute field mean?

  • From minute 5 onward
  • Every 5 minutes
  • Only on the 5th
  • At 5 minutes past the hour

Answer: Every 5 minutes. The step syntax */5 in the minute field means run every 5 minutes.

What does the line 0 9 * * 1-5 do?

  • 9 PM every day
  • Every 9 minutes on weekdays
  • At minute 9 every hour
  • At 9 AM Monday through Friday

Answer: At 9 AM Monday through Friday. minute 0, hour 9, any day-of-month, any month, days 1-5 (Mon-Fri) means 9:00 AM on weekdays.

What does @reboot do in a crontab?

  • Runs the job once at system startup
  • Runs the job every minute
  • Disables the job
  • Reboots the machine on a schedule

Answer: Runs the job once at system startup. @reboot runs the job a single time when the system boots, not on a clock schedule.

Which value range is correct for the hour field?

  • 1-12
  • 0-23
  • 1-24
  • 0-12

Answer: 0-23. Hours run 0-23 in 24-hour form; 0 is midnight and 23 is 11 PM.

Why do cron jobs often fail even though the same command works in your shell?

  • cron only runs as root
  • cron cannot run shell scripts
  • cron caps jobs at one per hour
  • cron has a minimal environment and PATH, so use absolute paths

Answer: cron has a minimal environment and PATH, so use absolute paths. cron runs with a sparse PATH and not your login environment, so commands and scripts should use absolute paths (or set PATH at the top of the crontab).

What does the MAILTO setting in a crontab do?

  • Schedules an email job
  • Logs output to a file
  • Sets the email address that receives cron output
  • Sends a test email immediately

Answer: Sets the email address that receives cron output. MAILTO=you@example.com sets who receives a job's stdout/stderr; MAILTO= (empty) disables that mail.

Which schedule runs at midnight on the 1st of every month?

  • 0 0 * * 1
  • 0 0 1 * *
  • * * 1 * *
  • 0 1 0 * *

Answer: 0 0 1 * *. minute 0, hour 0, day-of-month 1, any month, any weekday means 00:00 on the 1st of each month.

Which command schedules a one-time job to run once at a future time?

  • at
  • crontab -r
  • cron once
  • watch

Answer: at. at runs a command a single time at a future moment; cron is for jobs that repeat on a schedule.

Continue this course

Frequently asked questions

What is the difference between crontab -e, -l and -r?

crontab -e EDITS your crontab in an editor (this is how you add or change jobs). crontab -l LISTS (prints) it so you can see what is scheduled. crontab -r REMOVES it — and it deletes the ENTIRE crontab without asking, which is dangerous because -r sits right next to -e on the keyboard. Back up first with crontab -l > backup.txt.

What is the order of the five cron schedule fields?

Left to right: minute (0-59), hour (0-23), day-of-month (1-31), month (1-12), and day-of-week (0-7, where both 0 and 7 mean Sunday), followed by the command. So 30 2 * * * means minute 30, hour 2, any day, any month, any weekday — i.e. 2:30 AM every day.

Why does my cron job do nothing even though the command works when I type it?

Almost always the environment. cron runs with a minimal PATH (often just /usr/bin:/bin) and does NOT load your ~/.bashrc or login profile, so a bare command name may not be found. Fix it by using absolute paths (for example /usr/bin/python3 /home/user/app/job.py), or set PATH at the top of the crontab. Also redirect output to a log with >> file 2>&1 so you can actually see the error.

Where does the output of a cron job go?

By default cron emails a job's stdout and stderr to the local user. You can set MAILTO=you@example.com at the top of the crontab to choose the recipient, or MAILTO= (empty) to turn that mail off. Most people instead append output to a log file, for example: 0 3 * * * /path/job.sh >> /var/log/job.log 2>&1 — where 2>&1 sends errors to the same log.

When should I use cron versus at or systemd timers?

Use cron for jobs that REPEAT on a schedule (every day, every 15 minutes, every Monday). Use at to run a command ONCE at a single future time. On modern systemd-based Linux, systemd timers are an alternative to cron that add features like dependency ordering, logging through journald, and catch-up runs for missed jobs.

Related lessons