Developer Tools

Cron Expressions Explained -Complete Guide with Examples

EveryTool Editorial
8 min read

Cron is one of the oldest and most ubiquitous scheduling systems in computing. Originally developed for Unix systems in the 1970s, cron jobs are now found in Linux servers, cloud platforms, CI/CD pipelines, Kubernetes clusters, and countless web applications. The cron expression format -a compact five-field syntax that encodes an arbitrarily complex recurring schedule -has remained largely unchanged for decades. Yet it trips up developers constantly. This guide explains every aspect of cron expressions clearly, with examples you can immediately apply.

The Five Fields of a Cron Expression

A standard cron expression consists of five space-separated fields. Reading left to right: the first field specifies the minute (0–59), the second specifies the hour (0–23), the third specifies the day of the month (1–31), the fourth specifies the month (1–12), and the fifth specifies the day of the week (0–7, where both 0 and 7 represent Sunday). A helpful mnemonic is 'minutes, hours, date, month, weekday.' Some systems extend this with a seconds field at the beginning and a year field at the end.

Memory trick: think of cron fields as zooming out in time -minutes → hours → days → months → weekdays. The most granular (seconds, minutes) comes first.

Special Characters -The Real Power of Cron

  • **`*` (wildcard)** -Matches every valid value in the field. `* * * * *` means every minute of every day. Use when you do not want to restrict a field.
  • **`,` (list)** -Specifies multiple values. `1,3,5` in the day-of-week field means Monday, Wednesday, and Friday. Can have as many values as needed.
  • **`-` (range)** -Specifies an inclusive range. `9-17` in the hours field means every hour from 9 AM to 5 PM. Can be combined with list: `1-5,7`.
  • **`/` (step)** -Specifies a step increment. `*/5` means every 5 units. `10/5` means every 5 units starting at 10. In minutes `*/15` means 0, 15, 30, 45.
  • **`L` (last)** -In the day-of-month field `L` means the last day of the month. In the day-of-week field `5L` means the last Friday of the month. Not supported in standard Unix cron.
  • **`W` (weekday)** -Nearest weekday to a given date. `15W` means the nearest weekday to the 15th. If the 15th is Saturday the job runs on Friday the 14th. Not in standard Unix cron.
  • **`#` (nth weekday)** -`1#3` means the third Monday of the month. `5#2` means the second Friday. Supported in Quartz but not standard cron.
  • **`?` (no specific value)** -Used in Quartz and AWS to say 'I do not care about this field.' Required in one of dom or dow when the other is specified.

Common Cron Expressions You Will Actually Use

  • `* * * * *` -Every minute. Useful for high-frequency background tasks like queue processing.
  • `*/5 * * * *` -Every 5 minutes. Good for polling or sync tasks.
  • `0 * * * *` -At the top of every hour. Common for hourly reports or cache refreshes.
  • `0 0 * * *` -Daily at midnight. Good for daily cleanup or batch processing.
  • `0 9-17 * * 1-5` -Every hour from 9 AM to 5 PM on weekdays. For business-hours notifications.
  • `0 0 1 * *` -First of every month at midnight. For monthly billing or reports.
  • `0 0 * * 0` -Every Sunday at midnight. Good for weekly maintenance.
  • `0 2 * * *` -Every day at 2 AM. A common time for backups and heavy maintenance.
  • `30 6 */2 * *` -Every other day at 6:30 AM. For semi-frequent scheduled tasks.
  • `0 0 1 1 *` -January 1st at midnight. For yearly operations.

DOM vs DOW -A Common Source of Confusion

One of the most frequently misunderstood aspects of cron is the relationship between the day-of-month (dom) and day-of-week (dow) fields. In standard Unix cron, if you specify a value in both fields (rather than `*`), the job runs when EITHER condition is true -it is OR logic, not AND. So `0 0 15 * 1` runs at midnight on the 15th of every month AND also every Monday at midnight, not on Mondays that fall on the 15th. If you want AND logic (only on Mondays that are also the 15th) you need to handle that in your script, not the cron expression. Quartz scheduler adds the `?` character specifically to address this -using `?` in one field means you are restricting by the other only.

Platform Differences You Need to Know

  • Standard Unix/Linux cron -5 fields (min hour dom mon dow). Day-of-week 0 and 7 both mean Sunday. No L, W, or # characters.
  • Quartz Scheduler (Java) -6 or 7 fields: seconds min hour dom mon dow [year]. Uses ? for no-specific-value in dom or dow. Day-of-week uses 1=Sunday convention (opposite of Unix).
  • AWS EventBridge (CloudWatch Events) -6 fields including year. Requires ? in either dom or dow. Uses cron() wrapper syntax: `cron(0 12 * * ? *)`.
  • GitHub Actions -Standard 5-field POSIX cron. Minimum interval is every 5 minutes. Schedules run in UTC.
  • Kubernetes CronJob -Standard 5-field Unix cron. Times are in the timezone of the kube-controller-manager unless configured otherwise.
  • Vercel Cron (vercel.json) -Standard 5-field. Minimum interval is every minute on Pro plans.

Always check which cron implementation your platform uses. Quartz day-of-week values are offset by 1 compared to Unix (Quartz 1=Sunday vs Unix 0=Sunday). A mistake here causes your job to run on the wrong day entirely.

Tips for Writing Reliable Cron Expressions

  • Always test your expression with a tool before deploying -verify the next run times are exactly what you expect
  • Be explicit about timezone -most cron systems use the server's local timezone or UTC. Document which one applies and set it explicitly if possible.
  • Avoid scheduling many jobs at the same second -midnight (0 0 * * *) is the most common cron time. Consider offsetting by a few minutes to spread load.
  • Add jitter for distributed systems -if multiple instances run the same cron, add randomized delay in your job code to avoid thundering herd
  • Test edge cases -what happens at the end of month? February 29th? DST transitions?
  • Log every execution -cron jobs often fail silently. Always log start, end, and any errors.

Frequently Asked Questions

What does 0 * * * * mean in cron?

It runs at minute 0 of every hour -i.e. every hour on the hour (1:00, 2:00, 3:00, etc.). The 0 in the first field pins the minute to exactly 0, and all other fields are wildcards.

How do I run a cron job every 30 minutes?

Use `*/30 * * * *`. This runs at minute 0 and minute 30 of every hour. Alternatively `0,30 * * * *` produces the same result using list notation.

What is the minimum cron interval?

In standard cron the minimum interval is every minute (`* * * * *`). For sub-minute scheduling you need a system that supports the seconds field, like Quartz. Some platforms like GitHub Actions enforce a minimum of every 5 minutes.

How do I run a cron job on the first Monday of the month?

This is not directly expressible in standard 5-field cron without using `#`. In Quartz use `0 0 ? * 2#1` (first Monday). In standard cron you must use `0 0 1-7 * 1` (first 7 days of month AND Monday) which works because of OR logic between dom and dow. Or handle it in the job script by checking the date.

Why is my cron job not running?

Common causes: wrong timezone assumption, field order mistake (hours and minutes swapped), using Quartz syntax on a Unix cron system, specifying a value out of range (like day 31 for a month with 30 days), the cron daemon not running, or file permission issues on the script.

Tools Mentioned in this Article