Skip to main content
Blog|
Learning center

Cron Job Syntax: A Practical Guide with Examples

|
Mar 19, 2026|11 min read
LEARNING CENTERCron Job Syntax: A PracticalGuide with ExamplesHOSTNEYhostney.comMarch 19, 2026

Cron is the standard task scheduler on Linux and macOS. It runs commands automatically on a schedule you define. Backups at 2 AM, log cleanup every Sunday, database optimization once a month. You write one line in a crontab file and the system handles the rest.

The syntax is compact but not intuitive. The five-field time expression looks like line noise until you learn what each field means. This guide breaks down the syntax, walks through common schedules with copy-paste examples, and covers the practical issues that cause cron jobs to silently fail.

The five-field cron syntax#

Every cron job is a single line with two parts: a time expression and a command.

* * * * * /path/to/command

The five asterisks represent five time fields, left to right:

┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, where 0 and 7 are Sunday)
│ │ │ │ │
* * * * * command

An asterisk means “every.” So * * * * * means every minute of every hour of every day. Each field can be replaced with a specific value, a range, a list, or a step value.

Field values

FieldAllowed valuesNotes
Minute0-59
Hour0-230 is midnight, 13 is 1 PM
Day of month1-31
Month1-12Can also use names: jan, feb, etc.
Day of week0-70 and 7 are both Sunday. Can also use names: mon, tue, etc.

Special characters

Asterisk (*) – matches every value in the field.

* * * * *     # every minute

Comma (,) – specifies a list of values.

0 8,12,18 * * *     # at 8:00, 12:00, and 18:00

Hyphen (-) – specifies a range.

0 9-17 * * *     # every hour from 9:00 to 17:00

Slash (/) – specifies a step value. Used with ranges or asterisks.

*/5 * * * *     # every 5 minutes
*/15 * * * *    # every 15 minutes
0 */2 * * *     # every 2 hours (at minute 0)

These can be combined:

0,30 9-17 * * 1-5     # at :00 and :30, 9 AM to 5 PM, Monday through Friday

Common schedules#

These are the schedules that cover the vast majority of real-world cron jobs.

Every minute

* * * * * /path/to/command

Use this sparingly. Running a command 1,440 times per day generates load and fills logs fast. Health checks and queue processors are the common legitimate use cases.

Every 5 minutes

*/5 * * * * /path/to/command

A common interval for checking services, processing queues, or running WordPress cron. The */5 means “every minute that is divisible by 5” (0, 5, 10, 15, …).

Every 15 minutes

*/15 * * * * /path/to/command

Every 30 minutes

*/30 * * * * /path/to/command

Note: */30 fires at minute 0 and minute 30 of every hour. It is not “every 30 minutes from when the cron job was created.” Cron does not track when you added the entry. It evaluates the time expression against the current system time.

Hourly

0 * * * * /path/to/command

Runs at minute 0 of every hour. If you use * * * * * thinking it means hourly, it runs every minute. The 0 in the minute field is what makes it hourly.

Daily at a specific time

0 2 * * * /path/to/command

Runs at 2:00 AM. The time is based on the server’s timezone. If your server is UTC and you want 2:00 AM Eastern, you need to calculate the offset.

Daily at midnight

0 0 * * * /path/to/command

Twice daily

0 6,18 * * * /path/to/command

Runs at 6:00 AM and 6:00 PM.

Weekly (every Sunday)

0 3 * * 0 /path/to/command

Runs at 3:00 AM every Sunday. You can use 7 instead of 0 for Sunday, or use day names:

0 3 * * sun /path/to/command

Weekly (every Monday)

0 3 * * 1 /path/to/command

Weekdays only (Monday through Friday)

0 9 * * 1-5 /path/to/command

Runs at 9:00 AM, Monday through Friday.

Monthly (first of the month)

0 4 1 * * /path/to/command

Runs at 4:00 AM on the 1st of every month.

Quarterly

0 4 1 1,4,7,10 * /path/to/command

Runs at 4:00 AM on the 1st of January, April, July, and October.

Yearly (January 1st)

0 0 1 1 * /path/to/command

Runs at midnight on January 1st.

Editing the crontab#

crontab -e

To edit the current user’s cron jobs:

crontab -e

This opens the crontab file in your default editor (usually nano or vi). Add one job per line. Save and exit. The cron daemon picks up changes automatically – no restart needed.

If it is your first time, the system may ask which editor to use. Choose nano if you are not familiar with vi.

crontab -l

To list all cron jobs for the current user:

crontab -l

This prints the contents of the crontab to the terminal. If no crontab exists, it prints “no crontab for username.”

Edit another user’s crontab

To edit cron jobs for a specific user (requires root):

sudo crontab -u www-data -e

To list another user’s cron jobs:

sudo crontab -u www-data -l

This is common for web server tasks. PHP scripts that need to run on a schedule should typically be in the www-data user’s crontab (or whatever user PHP-FPM runs as) so they have the correct file permissions.

Remove all cron jobs

crontab -r

This deletes the entire crontab for the current user with no confirmation prompt. There is no undo. If you want to temporarily disable all jobs without deleting them, comment them out with # in crontab -e instead.

Back up the crontab

Before making significant changes:

crontab -l > ~/crontab-backup.txt

To restore:

crontab ~/crontab-backup.txt

System crontab vs user crontab#

There are two types of crontab files on a Linux system.

User crontabs are edited with crontab -e . Each user has their own. Commands run as that user. These are stored in /var/spool/cron/crontabs/ (Ubuntu/Debian) or /var/spool/cron/ (CentOS/RHEL). Do not edit these files directly.

The system crontab is /etc/crontab . It has a sixth field between the time expression and the command: the username to run the command as.

0 2 * * * root /usr/local/bin/backup.sh

The root between the schedule and the command specifies which user executes it. User crontabs do not have this field because the user is implicit.

Additionally, scripts placed in /etc/cron.daily/ , /etc/cron.hourly/ , /etc/cron.weekly/ , and /etc/cron.monthly/ directories run on those schedules automatically without needing crontab entries. The scripts must be executable and should not have file extensions.

Environment variables in cron#

Cron jobs run in a minimal environment that is different from your interactive shell. This is the single most common reason cron jobs fail. A command that works perfectly when you run it manually in SSH fails silently when cron runs it.

PATH is minimal

When you log in via SSH, your shell loads ~/.bashrc , ~/.bash_profile , and /etc/profile , which set up your PATH to include directories like /usr/local/bin , /home/user/.local/bin , and others. Cron does not load these files. Its default PATH is typically just:

/usr/bin:/bin

A command like wp cron event run works in your shell because WP-CLI is installed in /usr/local/bin/wp , which is in your PATH. Cron cannot find it because /usr/local/bin is not in cron’s PATH.

Fix: use full paths to commands.

# Bad - cron may not find the command
0 2 * * * wp cron event run --due-now

# Good - full path to the binary
0 2 * * * /usr/local/bin/wp cron event run --due-now

To find the full path of a command:

which wp

Or set PATH at the top of the crontab:

PATH=/usr/local/bin:/usr/bin:/bin

0 2 * * * wp cron event run --due-now

Variables set at the top of the crontab apply to all jobs below them.

SHELL defaults to /bin/sh

Cron uses /bin/sh by default, not bash. If your command uses bash-specific syntax (arrays, [[ conditionals, process substitution), it will fail. Either set the shell in the crontab:

SHELL=/bin/bash

Or invoke bash explicitly:

0 2 * * * /bin/bash /path/to/script.sh

MAILTO

By default, cron emails the output of every job to the crontab owner. If the mail system is not configured (common on most servers), these emails queue up silently and waste disk space.

To disable email notifications:

MAILTO=""

To send to a specific address:

MAILTO="admin@example.com"

Place these at the top of the crontab, before any job entries.

Setting custom variables

You can define any environment variable at the top of the crontab:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
WP_PATH=/var/www/html

0 2 * * * cd $WP_PATH && wp cron event run --due-now

Logging cron output#

By default, cron captures the standard output and standard error of each job and tries to email it. If you are not receiving those emails (you probably are not), the output is lost.

Redirect output to a log file

0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1

>> appends standard output to the log file. 2>&1 redirects standard error to the same file. This captures both normal output and error messages.

Use >> (append) rather than > (overwrite) so you keep a history of runs rather than only the most recent one.

Log with timestamps

Cron itself does not add timestamps to output. Add them in the command:

0 2 * * * echo "$(date): Starting backup" >> /var/log/backup.log 2>&1 && /path/to/backup.sh >> /var/log/backup.log 2>&1

Or better, add timestamps inside the script itself.

Discard output entirely

If you do not care about the output and want to suppress the email:

0 2 * * * /path/to/command > /dev/null 2>&1

This sends both stdout and stderr to /dev/null . Only do this for commands you trust to work reliably, because you will never know if they start failing.

Check the system log

Cron logs job execution (but not output) to the system log. On Ubuntu:

grep CRON /var/log/syslog

On CentOS/RHEL:

grep CRON /var/log/cron

This shows when each job started. It does not show whether the job succeeded or what it output.

Common mistakes#

Forgetting that cron uses a different PATH

Already covered above, but worth repeating because it is the number one cause of cron job failures. If a command works in your shell but not in cron, it is almost always a PATH issue.

Missing execute permission on scripts

If your cron job runs a script:

chmod +x /path/to/script.sh

Without execute permission, cron cannot run the script. You will see “Permission denied” in the system log.

Incorrect working directory

Cron jobs start in the user’s home directory, not in the directory where the script lives. If your script references relative file paths, it will look for them relative to the home directory.

# Bad - relative path depends on working directory
0 2 * * * ./backup.sh

# Good - absolute path or explicit cd
0 2 * * * /home/user/scripts/backup.sh
0 2 * * * cd /home/user/scripts && ./backup.sh

For WordPress WP-CLI commands, always cd to the WordPress directory first:

0 2 * * * cd /var/www/html && /usr/local/bin/wp cron event run --due-now

Jobs overlapping

If a cron job takes longer to run than the interval between runs, you get multiple instances running simultaneously. A backup that takes 20 minutes on a 15-minute schedule results in overlapping runs competing for resources.

Use flock to prevent overlapping:

*/15 * * * * /usr/bin/flock -n /tmp/mybackup.lock /path/to/backup.sh

The -n flag makes flock exit immediately if the lock is already held. The job simply skips that interval instead of queuing up.

Editing /var/spool/cron directly

Never edit crontab files directly in /var/spool/cron/ . Always use crontab -e . The crontab -e command validates syntax before saving and notifies the cron daemon of changes. Direct file edits skip validation and may not be picked up by the daemon until it is restarted.

Percent signs in commands

In crontab, the % character is interpreted as a newline. If your command contains a literal percent sign (common in date format strings), it breaks the command.

# Bad - % is interpreted as newline
0 2 * * * /path/to/command --date=$(date +%Y-%m-%d)

# Good - escape the percent signs
0 2 * * * /path/to/command --date=$(date +\%Y-\%m-\%d)

Or put the command in a script and call the script from cron, which avoids the percent sign issue entirely.

Practical examples#

WordPress cron replacement

Replace WordPress’s built-in wp-cron with a real cron job. In wp-config.php, disable the web-triggered cron:

define('DISABLE_WP_CRON', true);

Then add to the server crontab:

*/5 * * * * cd /var/www/html && /usr/local/bin/wp cron event run --due-now >> /var/log/wp-cron.log 2>&1

This runs pending WordPress scheduled tasks every 5 minutes. It is more reliable than wp-cron.php because it does not depend on site traffic and does not add overhead to page loads.

Database backup

0 3 * * * /usr/bin/mysqldump -u backup_user wordpress | /bin/gzip > /backups/wordpress-$(date +\%Y\%m\%d).sql.gz 2>> /var/log/backup-errors.log

Runs at 3:00 AM daily. Dumps the WordPress database, compresses it, and saves with a date-stamped filename.

rsync backup on a schedule

0 4 * * * /usr/bin/rsync -az --delete /var/www/html/ /backups/website/ >> /var/log/rsync-backup.log 2>&1

Runs at 4:00 AM daily. Syncs the website directory to a backup location. Only changed files are transferred on subsequent runs. See How to sync directories with rsync for the full breakdown of rsync flags and directory sync patterns.

Clean up old log files

0 5 * * 0 /usr/bin/find /var/log/myapp/ -name "*.log" -mtime +30 -delete

Runs at 5:00 AM every Sunday. Deletes log files older than 30 days.

Remote command via SSH

0 2 * * * /usr/bin/ssh user@example.com "cd /var/www/html && wp cron event run --due-now" >> /var/log/remote-cron.log 2>&1

Runs a command on a remote server at 2:00 AM. Requires SSH key authentication since cron cannot enter a password. See How to run commands over SSH for syntax details and how to handle quoting and environment variables in remote commands.

Cron jobs on Hostney#

On Hostney, cron jobs are managed through the control panel rather than through SSH and crontab -e . Access the scheduler under the Cron Jobs section in the control panel.

You set the schedule using the same five-field syntax covered in this guide, and the command runs inside your account’s isolated container with the correct PATH and permissions already configured. Output and errors are visible in the control panel’s logs.

For SSH access to your container (useful for testing commands before scheduling them), see the Terminal Access section in the control panel.