Cron and Crontab Syntax: Schedule Jobs on Linux with Examples

System · 8 min read · Updated 25 Oct 2026

On this page
  1. Editing your crontab
  2. The five time fields
  3. Common schedules
  4. Why jobs work by hand but fail in cron
  5. The percent sign trap
  6. Logging and output
  7. Stop jobs from overlapping
  8. System crontabs and cron directories
  9. Quick reference

Cron is the Linux scheduler. A small daemon wakes up every minute, reads a set of tables called crontabs, and runs any command whose time has come. Nightly backups, log cleanups, certificate renewals and "check this every five minutes" scripts are almost always cron jobs. The syntax is compact and easy to get slightly wrong, so this guide covers both the format and the traps that make jobs silently fail.

Editing your crontab

$ crontab -e     # edit your crontab in $EDITOR
$ crontab -l     # list it
$ crontab -r     # remove it entirely (no confirmation!)

Each user has their own crontab, and jobs run as that user. crontab -e opens a temporary copy in your editor and installs it when you save; if there is a syntax error, it tells you and offers to edit again. Root can manage another user's table with sudo crontab -u alice -e.

crontab -r sits right next to -e on the keyboard and deletes everything without asking. Keep a backup with crontab -l > ~/crontab.bak, and restore it with crontab ~/crontab.bak.

If crontab -e opens an editor you do not know, set EDITOR first, for example EDITOR=nano crontab -e. The environment variables guide explains how to make that permanent.

The five time fields

Every job line has five time fields followed by the command:

# ┌───────── 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; 0 and 7 are Sunday)
# │ │ │ │ │
  * * * * *  command to run

Each field accepts:

  • * for every value.
  • A single number, such as 5.
  • A list, such as 1,15,30.
  • A range, such as 1-5 (Monday to Friday in the last field).
  • A step, such as */10 (every 10th value) or 0-30/5.

Common schedules

ScheduleMeaning
* * * * *Every minute
*/5 * * * *Every five minutes
0 * * * *At the start of every hour
30 2 * * *Every day at 02:30
0 9 * * 1-509:00 on weekdays
0 0 * * 0Midnight every Sunday
0 3 1 * *03:00 on the first of every month
15 6 1 1 *06:15 on 1 January
0 8-18/2 * * *Every two hours from 08:00 to 18:00

Most cron implementations also accept shortcuts in place of the five fields: @hourly, @daily, @weekly, @monthly, @yearly and @reboot, which runs once when the cron daemon starts after boot.

One quirk catches people out: if you restrict both the day of month and the day of week, cron runs the job when either matches, not both. 0 0 13 * 5 runs on every 13th and on every Friday, not only on Friday the 13th. To get the "and", schedule on one field and test the other inside the command.

A full line in your crontab looks like this:

30 2 * * * /home/alice/bin/backup.sh >> /home/alice/backup.log 2>&1

Why jobs work by hand but fail in cron

This is the number one cron problem, and it is almost always the environment. Cron does not read your .bashrc or .profile, runs commands with /bin/sh rather than Bash, starts in your home directory and uses a minimal PATH (on Debian and Ubuntu, just /usr/bin:/bin). A command in /usr/local/bin, a Node or Python version manager, or a Bash-only feature can all fail.

The fixes:

  • Use full paths to commands and files. command -v php tells you where a program lives.
  • Set variables at the top of the crontab. They apply to all the lines below:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
  • Put the real work in a script with a #!/bin/bash line, make it executable, and call the script from cron. That keeps the crontab readable and lets you test the exact same thing by hand. The scripting basics guide covers writing one.
  • cd to the right directory inside the script, or use absolute paths, because cron starts in your home directory.

To reproduce cron's bare environment at the prompt, run the script with an almost empty environment: env -i /bin/sh -c '/home/alice/bin/backup.sh'. If it fails there, it will fail in cron.

The percent sign trap

In a crontab line, an unescaped % is turned into a newline and everything after it is sent to the command as input. So this common line breaks:

0 1 * * * tar -czf /backups/site-$(date +%F).tar.gz /var/www/site

Escape each percent sign with a backslash in the crontab, or move the command into a script where no escaping is needed:

0 1 * * * tar -czf /backups/site-$(date +\%F).tar.gz /var/www/site

Logging and output

Anything a job prints is emailed to the crontab's owner, or to the address in MAILTO. On servers without working local mail, that output simply disappears, which hides errors. Redirect it to a log instead:

*/10 * * * * /home/alice/bin/sync.sh >> /home/alice/logs/sync.log 2>&1

>> appends, and 2>&1 sends errors to the same file. Use > /dev/null 2>&1 only for jobs whose output you truly never need. The redirection guide explains the syntax.

To confirm that cron ran a job at all, check the system log:

$ journalctl -u cron --since "1 hour ago"    # Debian and Ubuntu
$ journalctl -u crond --since "1 hour ago"   # RHEL, Fedora, Rocky
$ grep CRON /var/log/syslog

Stop jobs from overlapping

If a job scheduled every five minutes sometimes takes ten, two copies end up running at once, which can corrupt backups or double-send emails. flock from util-linux takes a lock file and skips the run if the previous one still holds it:

*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /home/alice/bin/sync.sh

-n means "give up immediately if locked" instead of waiting.

System crontabs and cron directories

Besides per-user crontabs, there are system-wide ones that root manages directly:

  • /etc/crontab and files in /etc/cron.d/ have an extra field: the user to run as, between the time fields and the command.
  • /etc/cron.hourly, cron.daily, cron.weekly and cron.monthly hold executable scripts that run on that schedule. On Debian-based systems the file names may only contain letters, digits, underscores and hyphens, so backup.sh is silently skipped. run-parts --test /etc/cron.daily lists which scripts would actually run.

Many modern distributions also offer systemd timers, which add logging, dependencies and catch-up runs after downtime. Cron remains simpler for a one-line job, and it is everywhere.

Quick reference

GoalCommand or line
Edit your jobscrontab -e
List your jobscrontab -l
Back upcrontab -l > crontab.bak
Every 15 minutes*/15 * * * * cmd
Weekdays at 08:3030 8 * * 1-5 cmd
At boot@reboot cmd
Log outputcmd >> /path/job.log 2>&1
Date in a crontab$(date +\%F)
No overlapping runsflock -n /tmp/job.lock cmd
Did it run?journalctl -u cron