</>
DevToolHub
Dark terminal screen showing command line interface with green text

Crontab Syntax Explained: The Complete Cron Schedule Reference

·9 min read
Quick answer: A crontab expression has 5 fields: minute hour day-of-month month day-of-week. For example, 30 9 <em> </em> 1-5 runs at 9:30 AM every weekday. The <em> means "every," / means "step," - means "range," and , separates multiple values. Use the cron generator to build expressions visually.

I broke production at 3 AM once because I confused the day-of-month and month fields. Wrote 0 3 1 </em> <em> thinking it meant "every Monday at 3 AM." It actually meant "3 AM on the 1st of every month." The cleanup job only ran 12 times a year instead of 52. Nobody noticed for four months.

Cron syntax is simple — five fields, five rules. But mixing up which field is which costs real money in missed jobs and silent failures. This guide covers every field, every special character, and the expressions you'll actually use in practice.

The 5 Fields of a Cron Expression

Every cron expression is five fields separated by spaces. Left to right:

┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * *
FieldPositionAllowed ValuesDescription
Minute1st0-59Which minute of the hour
Hour2nd0-23Which hour of the day (24-hour format)
Day of month3rd1-31Which day of the month
Month4th1-12 or JAN-DECWhich month
Day of week5th0-6 or SUN-SATWhich day of the week (Sunday = 0 or 7)
Note that hours use 24-hour format. There's no AM/PM. 9 means 9 AM, 21 means 9 PM. If you write 9 thinking "9 PM," your job runs 12 hours early.

Also: both 0 and 7 represent Sunday in the day-of-week field. This varies by implementation, but every major cron supports both. Stick with 0 for consistency.

Special Characters: , /, -, and Comma

Four characters give cron its flexibility. Here's what each one does:

CharacterNameMeaningExampleReads As
<em>WildcardEvery possible value</em> <em> </em> <em> </em>Every minute of every hour of every day
/StepEvery Nth value<em>/15 </em> <em> </em> <em>Every 15 minutes
-RangeFrom X through Y0 9-17 </em> <em> </em>At minute 0, hours 9 through 17
,ListThese specific values0 0 1,15 <em> </em>Midnight on the 1st and 15th
You can combine them. 10-30/5 <em> </em> <em> </em> means "every 5 minutes between minutes 10 and 30" — so minutes 10, 15, 20, 25, 30. That's a range (10-30) with a step (/5).

The step operator / always starts from the beginning of the range unless paired with a - range. <em>/10 in the minute field means 0, 10, 20, 30, 40, 50 — not "10 minutes from now." This catches people who expect </em>/10 at 3:07 to mean 3:17. It doesn't. It fires at 3:10.

Reading Cron Expressions: 15 Real Examples

Theory is nice. Here are the expressions you'll actually write:

Every-N-Minutes Patterns

* * * * *        → Every minute
*/5 * * * *      → Every 5 minutes
*/15 * * * *     → Every 15 minutes
*/30 * * * *     → Every 30 minutes
0 * * * *        → Every hour (at minute 0)

Daily Patterns

0 0 * * *        → Midnight every day
0 6 * * *        → 6:00 AM every day
30 9 * * *       → 9:30 AM every day
0 9,18 * * *     → 9:00 AM and 6:00 PM every day
0 0,6,12,18 * * * → Every 6 hours (midnight, 6 AM, noon, 6 PM)

Weekly Patterns

0 9 * * 1        → 9:00 AM every Monday
0 9 * * 1-5      → 9:00 AM every weekday (Mon-Fri)
0 9 * * 0,6      → 9:00 AM every weekend (Sat, Sun)
0 22 * * 5       → 10:00 PM every Friday

Monthly and Yearly Patterns

0 0 1 * *        → Midnight on the 1st of every month
0 9 1,15 * *     → 9:00 AM on the 1st and 15th
0 0 1 1 *        → Midnight on January 1st (yearly)

Don't try to memorize these. Bookmark this page or use the cron generator to build expressions interactively. Translating English to cron syntax is error-prone when you're doing it at 11 PM during an incident.

Day-of-Month vs Day-of-Week: The Gotcha

When both day-of-month (field 3) and day-of-week (field 5) are set to non-wildcard values, most cron implementations run the job when either condition is true — not both.

0 9 15 * 1

You might read this as "9 AM on the 15th, but only if it's a Monday." Nope. It runs at 9 AM on every 15th of the month and at 9 AM on every Monday. That's roughly 8-9 executions per month, not 1-2.

This trips up experienced developers too. If you need "the 15th, only if it's a weekday," you can't express that in pure cron syntax. You have to check the day inside your script:

0 9 15 * * [ "$(date +\%u)" -le 5 ] && /path/to/script.sh

This runs the cron every 15th at 9 AM, but the script only executes if date +%u returns 1-5 (Monday through Friday).

Predefined Shortcuts

Most cron implementations support these shorthand expressions:

ShortcutEquivalentMeaning
@yearly0 0 1 1 <em>Midnight, January 1st
@monthly0 0 1 </em> <em>Midnight, 1st of every month
@weekly0 0 </em> <em> 0Midnight every Sunday
@daily0 0 </em> <em> </em>Midnight every day
@hourly0 <em> </em> <em> </em>Every hour at minute 0
@rebootOnce at system startup
@reboot is the odd one out — it has no cron equivalent because it's triggered by boot, not time. Useful for starting background services, but don't rely on it for critical tasks. Some cron implementations don't support it, and container restarts in Kubernetes won't trigger it.

These shortcuts are clearer than their five-field equivalents. @daily is instantly obvious; 0 0 <em> </em> <em> takes a second to parse. Use them when your cron implementation supports them.

Editing Your Crontab: The Basics

The crontab command manages per-user cron schedules.

crontab -e          # Edit your crontab (opens in $EDITOR)
crontab -l          # List your current crontab
crontab -r          # Remove your entire crontab (careful!)
crontab -u www-data -e  # Edit another user's crontab (root only)

A crontab file looks like this:

# Environment variables
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=dev@example.com

# Jobs
*/5 * * * * /home/deploy/scripts/health-check.sh
0 2 * * * /home/deploy/scripts/backup-db.sh >> /var/log/backup.log 2>&1
0 9 * * 1 /home/deploy/scripts/weekly-report.sh

Three things that bite people:

    • PATH is minimal. Cron doesn't load your .bashrc or .zshrc. If your script calls node or python3, use the full path (/usr/local/bin/node) or set PATH at the top of the crontab.
    • Output goes to email. If a cron job produces output (stdout or stderr), cron tries to email it to the user. On servers without a mail agent, this silently fails and fills up /var/spool/mail. Redirect output: >> /var/log/job.log 2>&1.
    • crontab -r deletes everything. There's no "undo." One typo — crontab -r instead of crontab -e — wipes your entire schedule. Back up with crontab -l > crontab-backup.txt before editing.

Timezone Behavior

Cron jobs run in the system's timezone by default. On a server set to UTC, 0 9 </em> <em> </em> is 9 AM UTC, not 9 AM your local time. This matters for:

  • Backup jobs that should run during off-peak hours in your users' timezone
  • Report emails that arrive before the team's workday starts
  • Any job tied to business hours
On most Linux distributions, you can check with timedatectl or cat /etc/timezone. If your cron implementation supports it (Vixie cron, cronie), add CRON_TZ=America/New_York to the crontab to override the system timezone for that file.

In cloud environments, assume UTC unless you've explicitly changed it. AWS EC2, GCP Compute, and most Docker images default to UTC. Converting between timezones in your head at 2 AM is how mistakes happen — set CRON_TZ and make it explicit. Need to convert timestamps? The Unix timestamp converter handles timezone offsets.

Debugging: When Your Cron Job Doesn't Run

Cron jobs fail silently. Here's a systematic checklist:

    • Check the cron log. On Ubuntu/Debian: grep CRON /var/log/syslog. On CentOS/RHEL: cat /var/log/cron. This tells you if cron even tried to run the job.
    • Run the command manually. Copy the exact command from crontab and run it in a terminal. If it fails there, it's a script problem, not a cron problem.
    • Check permissions. The script must be executable (chmod +x script.sh). The cron user must have read/execute access to the script and write access to any output files.
    • Check the PATH. Run which node or which python3 to find the full path, then use that in your crontab. node script.js works in your shell; /usr/local/bin/node /home/deploy/script.js works in cron.
    • Check the environment. Cron runs with a minimal environment. If your script depends on environment variables, define them in the crontab file or source them in the script.
    • Check for overlapping runs. If a job takes 10 minutes and runs every 5 minutes, you'll have overlapping instances. Use flock to prevent this: <em>/5 </em> <em> </em> <em> flock -n /tmp/job.lock /path/to/script.sh.

FAQ

What's the difference between system crontab and user crontab?

User crontabs (crontab -e) are per-user and have 5 fields before the command. The system crontab (/etc/crontab) and drop-in files in /etc/cron.d/ have 6 fields — the extra field between the day-of-week and the command specifies which user runs the job. Example: 0 2 </em> <em> </em> root /usr/local/bin/backup.sh. Regular users can't edit system crontabs.

Can I run a cron job every 45 seconds?

Cron's minimum resolution is 1 minute. For sub-minute scheduling, you have two options: (1) run the job every minute and have the script sleep internally (sleep 45 && /path/to/command), or (2) use systemd timers, which support arbitrary intervals down to microseconds. For most use cases, if you need sub-minute precision, you probably need a daemon or a message queue instead of cron.

How do I run a cron job on the last day of every month?

There's no "last day" syntax in cron. The cleanest workaround: 0 0 28-31 <em> </em> [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script.sh. This runs every day from the 28th through the 31st, but only executes the script if tomorrow is the 1st. Works for February (28th or 29th), 30-day months, and 31-day months.

Do cron jobs run if the server was off at the scheduled time?

No. Standard cron doesn't have "catch-up" or "makeup" behavior. If the server was off at 2 AM and your job was scheduled for 2 AM, it doesn't run when the server comes back at 3 AM. For critical jobs that must run even after downtime, use anacron (designed for machines that aren't always on) or move to a scheduler like Airflow or Kubernetes CronJobs, which track missed runs.

Next Steps

  • Build cron expressions visually with the cron generator — translate English schedules to cron syntax without guessing.
  • Working with epoch timestamps in your cron scripts? The Unix timestamp converter handles conversions between human-readable dates and Unix time.
  • Need to schedule tasks in a web context? Check our guide on building scheduled jobs with Node.js and cron libraries.