Crontab: Scheduling Cron Jobs in Linux

By 

Updated on

13 min read

Crontab cron job scheduling syntax and examples in Linux

Cron is a scheduling daemon that executes tasks at specified intervals. These tasks are called cron jobs and are most commonly used to automate system maintenance, run backups, and trigger recurring scripts.

For example, you could set a cron job to automate repetitive tasks such as backing up databases , updating the system with the latest security patches, checking disk space usage , or sending emails.

Cron jobs can be scheduled to run by minute, hour, day of the month, month, day of the week, or any combination of these.

Building a schedule? Our interactive crontab generator explains any cron expression in plain English and shows the next run times.

Quick Reference

For a printable quick reference, see the crontab cheatsheet .

ScheduleCron Expression
Every minute* * * * *
Every 5 minutes*/5 * * * *
Every 15 minutes*/15 * * * *
Every hour0 * * * *
Every day at midnight0 0 * * *
Every day at 9 AM0 9 * * *
Weekdays at 9 AM0 9 * * 1-5
Every Sunday at midnight0 0 * * 0
Every month on the 1st0 0 1 * *
Every year on Jan 1st0 0 1 1 *
When the cron daemon starts@reboot

What Is a Crontab File

Crontab (cron table) is a text file that specifies the schedule of cron jobs. There are two types of crontab files: system-wide crontab files and individual user crontab files.

User crontab files are named after the user and their location varies by distribution. On Red Hat, Fedora, and Derivatives, crontab files are stored in the /var/spool/cron directory. On Debian, Ubuntu, and Derivatives, they are stored in /var/spool/cron/crontabs.

Although you can edit user crontab files manually, it is recommended to use the crontab command.

The /etc/crontab file and the scripts inside the /etc/cron.d directory are system-wide crontab files that can only be edited by system administrators.

In most Linux distributions you can also place scripts inside the /etc/cron.{hourly,daily,weekly,monthly} directories, which run at the interval their name suggests. These directories have a few rules of their own, covered further down.

Crontab Syntax and Operators

Each line in a user crontab file contains five time and date fields separated by spaces, followed by the command to run:

txt
* * * * * command(s)
- - - - -
| | | | |
| | | | ----- Day of week (0 - 7) (Sunday=0 or 7)
| | | ------- Month (1 - 12)
| | --------- Day of month (1 - 31)
| ----------- Hour (0 - 23)
------------- Minute (0 - 59)

The first five fields may contain one or more values separated by a comma, or a range of values separated by a hyphen.

  • * - The asterisk means any value. An asterisk in the Hour field means the task runs every hour.
  • , - The comma allows you to specify a list of values. For example, 1,3,5 in the Hour field runs the task at 1 am, 3 am, and 5 am.
  • - - The hyphen specifies a range of values. For example, 1-5 in the Day of week field runs the task every weekday (Monday through Friday).
  • / - The slash specifies a step value. For example, */4 in the Hour field runs the task every four hours, equivalent to 0,4,8,12,16,20. You can also use a range: 1-30/10 is the same as 1,11,21.

Steps are evaluated only within the field they are applied to. Writing */23 in the Hour field does not mean “every 23 hours”. It means hour 0 and hour 23 of the same day, and then the count restarts at midnight.

Each field accepts a fixed range of values:

FieldAllowed values
Minute0-59
Hour0-23
Day of month1-31
Month1-12 or JAN-DEC
Day of week0-7 or SUN-SAT, where both 0 and 7 mean Sunday

Names are not case sensitive, so Mon, mon, and MON all work. Support for ranges and lists of names differs between implementations. On Fedora, RHEL, and derivatives, which ship cronie, mon-fri and jan,apr,jul are valid. On Debian, Ubuntu, and derivatives, ranges and lists of names are not allowed, so use the numeric form such as 1-5 there.

Day of Month and Day of Week

The day of month and day of week fields do not behave like the other three. When both fields are restricted, meaning neither one is an asterisk, cron runs the job when either field matches, not when both match.

This catches almost everyone once. Take this line:

txt
30 4 1,15 * 5 /path/to/script.sh

It reads like “4:30 AM on the 1st and 15th, but only on a Friday”. Cron reads it as “4:30 AM on the 1st and 15th of every month, plus 4:30 AM every Friday”. In a normal month that is roughly six runs, not zero or one.

Because of this rule, you cannot express “the first Monday of the month” with the time fields alone. Restrict the day of month to the first seven days, leave the day of week as an asterisk, and test the weekday inside the command:

txt
0 7 1-7 * * test $(date +\%u) -eq 1 && /path/to/script.sh

The 1-7 range limits the job to the first week of the month, and date +\%u returns the weekday as a number from 1 (Monday) through 7 (Sunday). Together they run the script only on the first Monday. The backslash in front of the percent sign is required, and the next section explains why.

Escaping the Percent Sign

The percent sign is not an ordinary character inside a crontab. Cron reads the command up to the first unescaped %, runs that part, and sends everything after it to the command as standard input, with each additional % turned into a newline.

This quietly breaks any command that uses date format specifiers. The following backup job does not do what it looks like:

txt
0 2 * * * /usr/bin/tar -czf /backup/site-$(date +%Y-%m-%d).tar.gz /var/www

Cron splits the line at the first %, so the shell receives the incomplete command tar -czf /backup/site-$(date + and exits with a syntax error before tar starts. Cron prepares the rest of the line as standard input, but there is no running command to receive it. Escape every percent sign with a backslash to pass it through to the shell:

txt
0 2 * * * /usr/bin/tar -czf /backup/site-$(date +\%Y-\%m-\%d).tar.gz /var/www

When a command needs more than a couple of percent signs, move it into a shell script and call the script from cron. Cron does not parse the contents of a script, so no escaping is needed inside it.

System-wide Crontab Files

The syntax of system-wide crontab files differs slightly from user crontabs. It contains an additional mandatory user field that specifies which user will run the cron job:

txt
* * * * * <username> command(s)

Cron Directories and anacron

Alongside crontab files, most distributions ship a set of directories that run scripts on a fixed schedule without any crontab entry of your own. Place an executable script in /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, or /etc/cron.monthly and it runs at that interval.

Two details decide whether the script actually runs. It needs the execute permission bit, and its filename must contain only letters, digits, underscores, and hyphens. run-parts, the helper that executes these directories, skips any name with a dot in it, so a script saved as backup.sh is ignored while backup runs.

On Debian, Ubuntu, and derivatives, the directories are driven by run-parts lines in /etc/crontab:

txt
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )

The test -x /usr/sbin/anacron guard is the part worth reading closely. If anacron is installed, the cron line does nothing and anacron takes over the daily, weekly, and monthly jobs instead. Fedora, RHEL, and derivatives reach the same result through /etc/cron.d/0hourly, which runs /etc/cron.hourly every hour, where a 0anacron script hands the longer intervals to anacron.

Anacron exists because cron assumes the machine is running at the scheduled moment. It records when each job last completed and catches up shortly after boot when a run was missed. That difference explains a common report: a daily job on a laptop or desktop never runs. Without anacron the run-parts line fires at 06:25, and a machine that is powered off then skips that day entirely.

Predefined Macros

There are several special cron schedule macros used to specify common intervals. You can use these shortcuts in place of the five-column date specification:

  • @yearly (or @annually) - Run once a year at midnight on January 1st. Equivalent to 0 0 1 1 *.
  • @monthly - Run once a month at midnight on the first day of the month. Equivalent to 0 0 1 * *.
  • @weekly - Run once a week at midnight on Sunday. Equivalent to 0 0 * * 0.
  • @daily (or @midnight) - Run once a day at midnight. Equivalent to 0 0 * * *.
  • @hourly - Run once an hour at the start of the hour. Equivalent to 0 * * * *.
  • @reboot - Run when the cron daemon starts, which normally happens during system boot. Restarting the daemon can run the job again.

crontab Command

The crontab command allows you to install, view , or open a crontab file for editing:

Warning
crontab -r deletes the entire crontab without confirmation. Back it up first with crontab -l > crontab.backup, or use crontab -r -i to require confirmation.
  • crontab -e - Edit the crontab file, or create one if it does not already exist.
  • crontab -l - Display the crontab file contents.
  • crontab -r - Remove your current crontab file.
  • crontab -r -i - Remove your current crontab file after asking for confirmation. The -i option only modifies -r, so it does nothing when used on its own.
  • sudo crontab -u <username> -e - Edit another user’s crontab file.

The crontab command opens the file using the editor specified by the VISUAL or EDITOR environment variables.

Crontab Variables

The cron daemon automatically sets several environment variables :

  • The default PATH is far shorter than the one in your interactive shell, and the exact value depends on the cron implementation. On Debian, Ubuntu, and derivatives it is /usr/bin:/bin. On Fedora, RHEL, and derivatives, which ship cronie, it is /usr/bin:/bin:/usr/sbin:/sbin. If the command you are running is not in that path, use the absolute path to the binary or set a custom PATH at the top of your crontab. You cannot implicitly append to $PATH as you would in a regular script, because cron does not expand variables in these assignments.
  • The default shell is /bin/sh. To use a different shell, set the SHELL variable at the top of your crontab.
  • Cron runs commands from the user’s home directory. Override this with the HOME variable.
  • Output is emailed to the crontab owner by default. Set MAILTO=email@example.com to redirect notifications, or set MAILTO="" to disable email entirely.

Crontab Restrictions

The /etc/cron.deny and /etc/cron.allow files allow you to control which users have access to the crontab command. Each file contains a list of usernames, one per line.

If /etc/cron.allow exists , only the users listed in it can use the crontab command. When that file does not exist but /etc/cron.deny does, everyone except the users listed in /etc/cron.deny can use the command.

If neither file exists, the behavior depends on the cron implementation. Standard Debian systems allow all users to use crontab, while some other implementations restrict access to the root user.

Cron Job Examples

If you are creating your first cron job, use this safe workflow:

  1. Open your crontab with crontab -e.
  2. Add one simple test job such as */5 * * * * date >> /tmp/cron-test.log.
  3. Save the file and confirm it is installed with crontab -l.
  4. Wait a few minutes and verify /tmp/cron-test.log is updated.

Once that works, the examples below cover the schedules you are most likely to need.

  • Run a command at 15:00 on every weekday (Monday through Friday):

    txt
    0 15 * * 1-5 command
  • Run a script every 5 minutes and redirect standard output to /dev/null so only errors are emailed:

    txt
    MAILTO=email@example.com
    */5 * * * * /path/to/script.sh > /dev/null
  • Run two commands every Monday at 3 PM:

    txt
    0 15 * * Mon command1 && command2
  • Run a PHP script every 2 minutes and append the output to a log file :

    txt
    */2 * * * * /usr/bin/php /path/to/script.php >> /var/log/script.log
  • Run a script every day, every hour on the hour, from 8 AM through 4 PM:

    txt
    0 08-16 * * * /path/to/script.sh
  • Run a script at 6 AM on the first day of every quarter:

    txt
    0 6 1 1,4,7,10 * /path/to/script.sh
  • Run a script at 9:15 PM on the 1st and 15th of every month:

    txt
    15 21 1,15 * * /path/to/script.sh
  • Set custom environment variables and run a command every minute:

    txt
    HOME=/opt
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    SHELL=/bin/bash
    MAILTO=email@example.com
    * * * * * command

Troubleshooting

Cron job does not run
Verify the syntax with crontab -l and test the command manually from the shell first. Also confirm that the cron daemon is running with systemctl status cron (Debian/Ubuntu) or systemctl status crond (Red Hat/Fedora).

Command works in the shell but not in cron
Cron uses a minimal PATH (/usr/bin:/bin). Use the absolute path to your binary (for example, /usr/bin/python3 instead of python3), or set a full PATH at the top of your crontab.

Script runs but produces no output or email
Cron sends output by email, which requires a working mail transfer agent. To capture output reliably, redirect it to a file: command >> /var/log/myjob.log 2>&1. To redirect both stdout and stderr, see the bash-redirect-stderr-stdout guide.

No sign the job ever started
Cron logs every job it launches, which separates a scheduling problem from a failing command. On Debian and Ubuntu, run journalctl -u cron --since today or grep CRON /var/log/syslog. On Fedora, RHEL, and derivatives, run journalctl -u crond --since today. If your command appears in the log, cron did its part and the failure is inside the command.

Jobs run at the wrong time
Cron uses the system timezone , which you can verify with timedatectl. Cronie supports CRON_TZ=Region/City at the top of a crontab to schedule that table in another timezone. Debian cron does not support per-crontab scheduling timezones: setting TZ changes the command environment but not when the job runs. On Debian, use the system timezone or check the target timezone inside a wrapper script.

Permission denied when running a script
Make the script executable with chmod +x /path/to/script.sh and ensure the cron user has read and execute permission on the file.

FAQ

How do I edit my crontab?
Run crontab -e. This opens your user crontab in the default editor. Save and exit to install the new schedule. Changes take effect immediately.

What is the difference between a user crontab and /etc/crontab?
User crontabs (managed with crontab -e) run jobs as that user and contain five time fields plus the command. The /etc/crontab and files in /etc/cron.d/ are system-wide, contain an additional username field, and can only be edited by root.

How do I redirect cron job output to a file?
Append >> /path/to/logfile.log 2>&1 to your cron command. The 2>&1 part redirects stderr to stdout so both are captured in the same file.

How do I run a cron job at system startup?
Use the @reboot macro instead of a time expression: @reboot /path/to/script.sh. The job runs when the cron daemon starts, normally during system boot. Because restarting the daemon can run it again, make sure the command is safe to repeat.

Conclusion

Cron is the standard tool for automating recurring tasks on Linux. Understanding the five-field time syntax, operators, and environment variables gives you full control over when and how your jobs run. For listing and managing existing jobs, see the crontab list guide .

Tags

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page