How to Schedule Tasks in Linux Using Cron

Cron is Linux’s task scheduler for running commands automatically at specified times. To schedule a task, run crontab -e to open your personal crontab file, then add a line using the format: minute hour day month weekday command. For example, 30 2 * * * /home/sarah/backup.sh runs a backup script every day at 2:30 AM, and 0 9 * * 1 /usr/bin/python3 /home/sarah/weekly_report.py runs a Python script every Monday at 9:00 AM. Use crontab -l to view your current scheduled tasks.

Automating Linux with Time

Every Linux system has tasks that need to happen on a schedule: backups at 3 AM, log files that need rotating weekly, monitoring scripts that check disk space every hour, database maintenance that runs at midnight, reports generated every Monday morning. Doing these tasks manually — remembering to run them, being at the computer at the right time — is fragile and unsustainable.

Cron is Linux’s answer to this problem. Cron is a time-based job scheduler that has been part of Unix systems since 1975. It runs in the background as a daemon, waking up every minute to check whether any scheduled tasks need to run. If a task is scheduled for the current time, cron executes it. If not, cron sleeps for another minute. This simple loop has powered Unix and Linux automation for five decades.

Understanding cron means understanding the crontab file format — a terse but logical syntax that specifies exactly when a command should run. Once you grasp the five time fields (minute, hour, day of month, month, day of week) and a few special characters, you can express virtually any schedule imaginable: every minute, every day at midnight, every weekday at 9 AM, the first day of every month, every 15 minutes between 9 AM and 5 PM.

This article covers cron completely: the daemon that runs it, the crontab syntax in depth, special scheduling shortcuts, system-wide cron configuration, environment considerations that trip up many first-time users, how to test and debug cron jobs, and when to consider systemd timers as an alternative.

How Cron Works: The Daemon

Cron runs as a background service (crond on Fedora, managed by systemd on most modern distributions):

$ systemctl status cron
● cron.service - Regular background program processing daemon
     Loaded: loaded (/lib/systemd/system/cron.service; enabled)
     Active: active (running) since Mon 2026-02-18 09:00:12 UTC

Every minute, the cron daemon:

  1. Reads all crontab files (per-user and system-wide)
  2. Checks each scheduled task against the current time
  3. Runs any tasks whose schedule matches the current minute
  4. Captures output and (if configured) mails it to the task owner

The cron daemon reads crontab files from:

  • /var/spool/cron/crontabs/ — per-user crontab files (one file per user)
  • /etc/crontab — the system-wide crontab
  • /etc/cron.d/ — additional system-wide crontab snippets
  • /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ — directories of scripts run at those intervals

The crontab Command: Managing Your Schedule

Each user has their own crontab (cron table) — a personal list of scheduled tasks that run as that user.

Opening and Editing Your Crontab

$ crontab -e

This opens your personal crontab in the editor defined by the EDITOR or VISUAL environment variable (usually nano or vi). If this is your first crontab, the file is empty with some explanatory comments.

Never edit crontab files directly at /var/spool/cron/crontabs/username — always use crontab -e. The crontab command validates the syntax before saving, and it notifies the cron daemon that the file has changed.

Viewing Your Current Crontab

$ crontab -l
# List all scheduled tasks for the current user
30 2 * * * /home/sarah/scripts/backup.sh
0 9 * * 1 /usr/bin/python3 /home/sarah/weekly_report.py
*/15 * * * * /home/sarah/scripts/check_disk.sh >> /var/log/disk_check.log 2>&1

Removing Your Crontab

$ crontab -r    # Remove ALL scheduled tasks — use with caution!

This deletes your entire crontab. There is no confirmation prompt by default. If you want to remove just one task, edit with crontab -e and delete that line.

Managing Another User’s Crontab (as root)

$ sudo crontab -u sarah -l       # List sarah's crontab
$ sudo crontab -u sarah -e       # Edit sarah's crontab

The Crontab Syntax: Five Time Fields

Each line in a crontab follows this structure:

┌─────────────── minute (0-59)
│  ┌──────────── hour (0-23)
│  │  ┌───────── day of month (1-31)
│  │  │  ┌────── month (1-12 or JAN-DEC)
│  │  │  │  ┌─── day of week (0-7 or SUN-SAT, both 0 and 7 = Sunday)
│  │  │  │  │
*  *  *  *  *  command to execute

Each field can contain:

  • A specific number (5, 14, 30)
  • An asterisk * meaning “every value” (every minute, every hour, etc.)
  • A comma-separated list of values (1,15,30 = at minute 1, 15, and 30)
  • A range with a hyphen (9-17 = hours 9 through 17)
  • A step value with a slash (*/5 = every 5, */15 = every 15)
  • Combinations (1-5,10,15 = 1 through 5, plus 10, plus 15)

Reading Crontab Entries: Examples

Every day at midnight:

0 0 * * * command

Minute 0, Hour 0, every day, every month, every weekday.

Every day at 2:30 AM:

30 2 * * * command

Minute 30, Hour 2, every day, every month, every weekday.

Every hour (at minute 0):

0 * * * * command

Minute 0, every hour, every day.

Every 15 minutes:

*/15 * * * * command

At minutes 0, 15, 30, and 45 of every hour.

Every 5 minutes:

*/5 * * * * command

At minutes 0, 5, 10, 15… 55 of every hour.

Every minute:

* * * * * command

All fields are * — runs at every minute.

Every weekday (Monday–Friday) at 9 AM:

0 9 * * 1-5 command

Minute 0, Hour 9, every day of month, every month, days 1 (Monday) through 5 (Friday).

Every Monday at 9 AM:

0 9 * * 1 command

Minute 0, Hour 9, every day, every month, only Monday (day 1).

First day of every month at midnight:

0 0 1 * * command

Minute 0, Hour 0, day 1 only, every month, every weekday.

Every 6 hours:

0 */6 * * * command

Minute 0, every 6th hour (0:00, 6:00, 12:00, 18:00).

Specific times: 9 AM and 5 PM, Monday through Friday:

0 9,17 * * 1-5 command

Minute 0, Hours 9 and 17, every day, every month, Monday through Friday.

Last day nuance: Cron cannot directly express “last day of month.” A workaround:

0 0 28-31 * * [ "$(date +\%d -d tomorrow)" = "01" ] && command

This runs on days 28–31 but only executes the command when tomorrow is the 1st (i.e., today is the last day of the month).

Day of Week Numbering

Both 0 and 7 represent Sunday. Monday is 1, Tuesday is 2, through Saturday is 6:

Number Day
0 or 7 Sunday
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday

You can also use three-letter abbreviations: SUN, MON, TUE, WED, THU, FRI, SAT.

Month Numbering and Names

Months 1–12 (January = 1, December = 12), or three-letter abbreviations: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC.

The Day-of-Month and Day-of-Week Interaction

An important, often-misunderstood behavior: when both day-of-month AND day-of-week are specified (not *), the job runs when EITHER condition is true — not when both are true simultaneously.

0 0 1 * 1 command

This does NOT mean “first Monday of the month.” It means “midnight on the 1st of every month OR midnight on every Monday” — whichever comes first.

To schedule for “the first Monday of the month,” you need a workaround:

0 0 * * 1 [ $(date +\%d) -le 7 ] && command

This runs every Monday but only executes the command if the day of the month is ≤ 7 (meaning it is in the first week).

Special Schedule Strings

Cron supports several special shorthand strings as alternatives to the five numeric fields:

String Meaning Equivalent
@reboot Run once at system startup —
@yearly or @annually Run once per year 0 0 1 1 *
@monthly Run once per month 0 0 1 * *
@weekly Run once per week 0 0 * * 0
@daily or @midnight Run once per day 0 0 * * *
@hourly Run once per hour 0 * * * *

These are much more readable for common schedules:

@daily   /home/sarah/scripts/daily_backup.sh
@weekly  /usr/local/bin/weekly_report.sh
@reboot  /home/sarah/scripts/startup_tasks.sh
@hourly  /home/sarah/scripts/check_services.sh

@reboot is particularly useful for tasks that should run every time the system starts — starting a background process, mounting a network drive, or running a one-time initialization.

Writing Effective Cron Jobs

Use Absolute Paths for Everything

Cron runs with a minimal environment that does not include your normal PATH. Commands that work perfectly in your terminal may fail in cron because cron cannot find them.

This may fail:

30 2 * * * backup.sh

This will work:

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

Inside your scripts, also use absolute paths for any commands:

#!/bin/bash
# In a script called by cron, use full paths:
/usr/bin/rsync -av /home/ /backup/
/bin/tar -czf /backup/archive.tar.gz /home/
/usr/bin/find /backup/ -mtime +30 -delete

Or explicitly set PATH at the top of your crontab file:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
30 2 * * * backup.sh    # Now works because PATH is set

Capture Output to a Log File

By default, cron emails command output to the user (using the local mail system). If no mail system is configured, output is discarded silently — making it impossible to know if your job ran or failed.

Always redirect output explicitly:

# Redirect both stdout and stderr to a log file
30 2 * * * /home/sarah/scripts/backup.sh >> /var/log/backup.log 2>&1

# Append to log (>>): keeps history
# Overwrite log (>): always shows just the latest run
30 2 * * * /home/sarah/scripts/check.sh > /tmp/check.log 2>&1

# Discard all output (use only for commands you know work correctly)
*/5 * * * * /home/sarah/scripts/heartbeat.sh > /dev/null 2>&1

The 2>&1 redirects stderr (file descriptor 2) to the same place as stdout (file descriptor 1), capturing error messages along with normal output.

Timestamping Log Output

For scripts called by cron, add timestamps to log entries so you can see exactly when each run happened:

#!/bin/bash
echo "=== Backup started: $(date) ==="
rsync -av /home/ /backup/
echo "=== Backup completed: $(date) ==="

MAILTO: Control Output Destination

Set MAILTO at the top of your crontab to control where cron sends job output:

MAILTO=""                      # Discard all output (no emails)
MAILTO="sarah@example.com"     # Send to this email address
MAILTO="sarah"                 # Send to local user "sarah"

Setting MAILTO="" silences all cron email notifications system-wide for your crontab.

Cron’s Environment

Cron runs jobs in a stripped-down environment very different from your interactive shell:

What Cron’s Environment Looks Like

HOME=/home/sarah       # Set to user's home directory
LOGNAME=sarah          # Username
USER=sarah             # Username
SHELL=/bin/sh          # sh, not bash, unless specified
PATH=/usr/bin:/bin     # Minimal PATH — not your full interactive PATH
MAILTO=sarah           # Default mail destination

Notice SHELL=/bin/sh — cron uses sh by default, not bash. If your script uses Bash-specific syntax (arrays, [[ ]] tests, process substitution), it may fail when run by cron even if it works fine in your terminal.

Solutions:

Use the shebang in your scripts to explicitly specify bash:

#!/bin/bash
# The shebang ensures bash is used regardless of SHELL

Or set SHELL in the crontab:

SHELL=/bin/bash
30 2 * * * backup.sh

Testing with Cron’s Environment

To test a command under conditions similar to cron’s minimal environment:

$ env -i HOME=/home/sarah PATH=/usr/bin:/bin SHELL=/bin/sh /home/sarah/scripts/backup.sh

env -i starts with an empty environment, then only the specified variables are set — approximating what cron sees.

System-Wide Cron Configuration

/etc/crontab: The System Crontab

The system crontab at /etc/crontab has an extra field compared to user crontabs — the username to run the command as:

$ cat /etc/crontab
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

The format: minute hour day month weekday user command

The user field specifies which user account the command runs as. System crontab entries typically run as root.

/etc/cron.d/: Structured System Cron Files

Package managers use /etc/cron.d/ to install cron jobs alongside their packages. Each file follows the same format as /etc/crontab (with a user field):

$ ls /etc/cron.d/
anacron  e2scrub_all  popularity-contest  sysstat

$ cat /etc/cron.d/sysstat
# The first element of the path is a directory where the debian-sa1
# script is located
PATH=/usr/lib/sysstat:/usr/sbin:/usr/sbin:/usr/bin:/sbin:/bin

# Activity reports every 10 minutes everyday
5-55/10 * * * * root command -v debian-sa1 > /dev/null && debian-sa1 1 1

/etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/

These directories contain executable scripts that run at the indicated interval. The cron daemon (or anacron) runs all scripts in these directories at the appropriate time:

$ ls /etc/cron.daily/
apport  apt-compat  aptitude  dpkg  logrotate  man-db  samba

To add a task that runs daily for all users (system-wide), drop an executable script in /etc/cron.daily/:

$ sudo nano /etc/cron.daily/my_maintenance_task
#!/bin/bash
# This script runs daily as root
find /tmp -type f -mtime +7 -delete
find /var/log -name "*.log.gz" -mtime +30 -delete
$ sudo chmod +x /etc/cron.daily/my_maintenance_task

anacron: For Systems Not Always Running

Standard cron requires the system to be running at the exact scheduled time. If the system is off at 2 AM when the daily backup is scheduled, the backup never runs.

anacron (irregular cron) solves this: it runs tasks based on how long ago they last ran rather than a specific clock time. When the system boots and anacron finds a daily task that has not run in more than 24 hours, it runs it shortly after boot.

$ cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# These replace cron's daily/weekly/monthly
1       5       cron.daily      run-parts --report /etc/cron.daily
7       10      cron.weekly     run-parts --report /etc/cron.weekly
@monthly  15    cron.monthly    run-parts --report /etc/cron.monthly

The fields are: period delay identifier command

  • period — days between runs (1 = daily, 7 = weekly)
  • delay — minutes to wait after boot before running
  • identifier — a name for the job (used to track last run time)

On modern Ubuntu, the /etc/cron.daily/, /etc/cron.weekly/, and /etc/cron.monthly/ scripts are handled by anacron rather than cron directly, ensuring they run even if the system was off at the scheduled time.

Troubleshooting Cron Jobs

Cron jobs failing silently is a common frustration. Systematic debugging usually finds the problem quickly.

Step 1: Check if Cron is Running

$ systemctl status cron
$ ps aux | grep cron

Step 2: Check the System Log

Cron logs its activity to the system log:

$ grep CRON /var/log/syslog | tail -20
Feb 18 02:30:01 myserver CRON[1234]: (sarah) CMD (/home/sarah/scripts/backup.sh)
Feb 18 02:30:03 myserver CRON[1235]: (sarah) END CMD (/home/sarah/scripts/backup.sh)

If you see “CMD” entries, cron is triggering the job. If you see errors, the log may show what went wrong.

Step 3: Check Output Logs

If you are capturing output to a log file:

$ tail -f /var/log/backup.log

If there are no logs, the command may be failing silently. Temporarily redirect output:

*/5 * * * * /home/sarah/test.sh >> /tmp/cron_debug.log 2>&1

Step 4: Test the Command Manually

Run the exact command from the crontab manually to confirm it works:

$ /home/sarah/scripts/backup.sh

Step 5: Test with Cron’s Environment

The most common reason cron jobs fail: they work manually but fail in cron’s stripped environment. Test with minimal environment:

$ env -i HOME=/home/sarah LOGNAME=sarah PATH=/usr/bin:/bin SHELL=/bin/sh /home/sarah/scripts/backup.sh

If this fails but the normal manual run works, the problem is PATH or environment. Fix by using absolute paths in the script or setting PATH in the crontab.

Step 6: Check File Permissions

The script must be executable:

$ ls -la /home/sarah/scripts/backup.sh
-rwxr-xr-x 1 sarah sarah 1234 Feb 18 09:00 backup.sh

If not executable: chmod +x /home/sarah/scripts/backup.sh

Step 7: Check the Script’s Shebang

Make sure the first line of the script correctly specifies the interpreter:

#!/bin/bash    # Correct for bash scripts

Without a shebang, /bin/sh is used — which may not support all bash features.

Practical Cron Examples

Daily Database Backup at 3 AM

0 3 * * * /usr/bin/pg_dump mydb > /backup/mydb_$(date +\%Y\%m\%d).sql 2>> /var/log/db_backup.log

Note: % characters in crontab must be escaped as \% — unescaped % is treated as a newline in crontab syntax.

Hourly Disk Space Check with Alert

0 * * * * /home/sarah/scripts/check_disk.sh
#!/bin/bash
THRESHOLD=90
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')

if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "WARNING: Disk usage at ${USAGE}% on $(hostname)" | mail -s "Disk Alert" admin@example.com
fi

Weekly Log Rotation and Cleanup

0 0 * * 0 find /var/log/myapp/ -name "*.log" -mtime +30 -exec gzip {} \;

Runs every Sunday at midnight, compressing log files older than 30 days.

Backup Before System Updates (Every Tuesday at 1 AM)

0 1 * * 2 /home/sarah/scripts/pre_update_backup.sh && sudo apt update && sudo apt upgrade -y

Sync Files Every 30 Minutes During Business Hours

*/30 9-17 * * 1-5 /usr/bin/rsync -aq /home/sarah/projects/ /nas/projects/

Runs every 30 minutes from 9 AM to 5 PM, Monday through Friday.

Run at System Startup

@reboot sleep 30 && /home/sarah/scripts/startup_services.sh

sleep 30 gives the system time to fully initialize before the script runs.

cron vs. systemd Timers

Modern Linux systems also offer systemd timers as an alternative to cron. Systemd timers are more feature-rich but more complex to set up:

Feature cron systemd timers
Setup complexity Low (one crontab line) Higher (two files: .service + .timer)
Logging Syslog journald (queryable with journalctl)
Missed jobs (if off) Not run (unless anacron) Can run on next boot
Per-job dependencies No Yes (run after network, etc.)
Randomized delay No Yes (RandomizedDelaySec=)
Persistent Via /etc/crontab systemctl enable timer_name.timer
On-calendar scheduling Yes Yes (more flexible expressions)

Use cron for: simple recurring tasks, quick setup, widespread compatibility. Use systemd timers for: tasks that need service dependencies, better logging with journalctl, or when you are already managing complex systemd services.

Quick Reference: Cron Syntax

Time Field Values

Field Range Special Values
Minute 0–59 *, */5, 0,30, 0-30
Hour 0–23 *, */6, 9-17
Day of Month 1–31 *, 1, 15, 1,15
Month 1–12 *, JAN–DEC, 1-6
Day of Week 0–7 (0,7=Sun) *, SUN–SAT, 1-5

Common Crontab Commands

Task Command
Edit crontab crontab -e
List crontab crontab -l
Remove all tasks crontab -r
Edit root crontab sudo crontab -e
Edit another user’s sudo crontab -u username -e
Check cron logs grep CRON /var/log/syslog

Quick Schedule Reference

Schedule Crontab Line
Every minute * * * * *
Every 5 minutes */5 * * * *
Every hour 0 * * * *
Every day at midnight 0 0 * * *
Every day at 2:30 AM 30 2 * * *
Every Monday 9 AM 0 9 * * 1
Weekdays 9 AM 0 9 * * 1-5
First of month midnight 0 0 1 * *
At system startup @reboot

Conclusion: Automate Everything That Repeats

Cron is one of the most practically valuable tools in Linux. The tasks that run automatically — backups, log management, monitoring, report generation, maintenance — free you from the cognitive burden of remembering to do them manually and eliminate the failures that come from human forgetfulness.

The core skill is understanding the five time fields well enough to express any schedule you can imagine. Once you can read */15 9-17 * * 1-5 command as “every 15 minutes during business hours on weekdays,” you have mastered the most important part of cron.

Pair that with two operational habits — always use absolute paths, and always capture output to a log file — and your cron jobs will run reliably and debuggably. The combination of a correctly specified schedule, a well-written script with a proper shebang and absolute paths, and output captured to a log file is the recipe for cron jobs that run without supervision indefinitely.

Hot this week

Understanding Pipes in Linux: Chaining Commands Together

Learn how Linux pipes work, how to chain commands together with the | operator, practical pipe examples, and the difference between pipes and redirection.

How to Redirect Output in Linux Commands

Learn how to redirect stdout, stderr, and stdin in Linux using >, >>, <, 2>, and 2>&1. Master output redirection with practical examples for scripts and everyday commands.

What Is the Linux Shell Profile and Bashrc File?

Learn the difference between .bashrc, .bash_profile, .profile, and /etc/profile in Linux — which files run when, what to put in each, and how to customize your shell startup.

How to Create and Edit Text Files from the Command Line

Learn every way to create and edit text files from the Linux command line — touch, cat, echo, redirection, heredocs, and terminal editors like nano and vim.

Understanding Linux Software Sources and PPAs

Learn what PPAs are, how Ubuntu software sources work, how to safely add and remove PPAs, and the risks and benefits of using third-party software repositories.

Topics

Understanding Pipes in Linux: Chaining Commands Together

Learn how Linux pipes work, how to chain commands together with the | operator, practical pipe examples, and the difference between pipes and redirection.

How to Redirect Output in Linux Commands

Learn how to redirect stdout, stderr, and stdin in Linux using >, >>, <, 2>, and 2>&1. Master output redirection with practical examples for scripts and everyday commands.

What Is the Linux Shell Profile and Bashrc File?

Learn the difference between .bashrc, .bash_profile, .profile, and /etc/profile in Linux — which files run when, what to put in each, and how to customize your shell startup.

How to Create and Edit Text Files from the Command Line

Learn every way to create and edit text files from the Linux command line — touch, cat, echo, redirection, heredocs, and terminal editors like nano and vim.

Understanding Linux Software Sources and PPAs

Learn what PPAs are, how Ubuntu software sources work, how to safely add and remove PPAs, and the risks and benefits of using third-party software repositories.

How to Search for Installed Packages in Linux

Learn how to check if a package is installed in Linux and list all installed packages using dpkg -l, rpm -qa, apt list --installed, and dnf list installed.

What Is the Difference Between apt and apt-get?

Learn the difference between apt and apt-get in Linux, when to use each, what commands changed, and why Ubuntu and Debian now recommend apt for interactive use over apt-get.

How to Check Disk Space in Linux

Learn how to check disk space in Linux using df, du, lsblk, and ncdu. Understand filesystem usage, find large files and directories, and free up disk space effectively.

Related Articles

Popular Categories