Cron Expression Explained: Syntax, Examples and Common Mistakes
Cron expressions explained: the five fields, ranges, lists and steps, ready-made examples, time zone and daylight saving traps, and how GitHub Actions differs.
A cron expression is five fields separated by spaces that tell a scheduler when to run a job: minute, hour, day of month, month and day of week. For example, 30 2 * * 1 means 02:30 every Monday. An asterisk means "every value", so * * * * * runs every minute. The command to run comes after the five fields.
Cron is used everywhere: Linux servers, hosting control panels, Laravel and other frameworks, Kubernetes and CI services such as GitHub Actions. The core syntax is the same, but the details around time zones, special characters and missed runs differ. This guide covers the standard format, examples you can copy, and the traps that cause jobs to run at the wrong time or not at all.
Key takeaways
- A standard cron expression has five fields: minute, hour, day of month, month and day of week, followed by the command.
- Use * for every value, commas for lists, hyphens for ranges and /n for steps, but steps reset at the end of each field, so */35 is not every 35 minutes.
- If both day of month and day of week are restricted, the job runs when either matches, which surprises many people.
- Check the time zone: GitHub Actions defaults to UTC, and daylight saving changes can skip or repeat jobs scheduled in local time.
The five fields
The Linux crontab(5) manual defines the fields like this:
| Position | Field | Allowed values |
|---|---|---|
| 1 | Minute | 0–59 |
| 2 | Hour | 0–23 |
| 3 | Day of month | 1–31 |
| 4 | Month | 1–12, or names such as jan–dec |
| 5 | Day of week | 0–7 (0 and 7 are both Sunday), or names such as mon, wed, fri |
Hours use the 24-hour clock, so 2 pm is 14. Names are the first three letters and are case-insensitive.
Read an expression left to right as "at this minute, of this hour, on this day of the month, in this month, on this day of the week". 0 9 * * 1-5 is "at minute 0 of hour 9, any day of the month, any month, Monday to Friday", in other words 09:00 on weekdays.
Special characters
| Character | Meaning | Example | Result |
|---|---|---|---|
* |
Every value ("first to last") | * * * * * |
Every minute |
, |
List | 0 9,17 * * * |
09:00 and 17:00 daily |
- |
Inclusive range | 0 8-18 * * * |
On the hour from 08:00 to 18:00 |
/ |
Step | */15 * * * * |
Minutes 0, 15, 30 and 45 |
You can combine them. 0-4,8-12 is a valid list of ranges, and 0-23/2 means every second hour.
Steps reset at the end of each field
The crontab manual warns that steps apply only within their own field. */35 in the minute field runs at minutes 0 and 35 of each hour, not every 35 minutes, because the count restarts at the top of every hour. If you need an interval that does not divide evenly into 60 minutes or 24 hours, cron alone cannot express it cleanly.
Shortcut macros
Many cron implementations accept named shortcuts. The crontab manual lists @reboot, @yearly (also @annually), @monthly, @weekly, @daily and @hourly. The Kubernetes CronJob documentation gives their equivalents:
| Macro | Equivalent | Runs |
|---|---|---|
@yearly |
0 0 1 1 * |
Midnight on January 1 |
@monthly |
0 0 1 * * |
Midnight on the first of each month |
@weekly |
0 0 * * 0 |
Midnight on Sunday |
@daily |
0 0 * * * |
Midnight every day |
@hourly |
0 * * * * |
At the start of every hour |
@reboot runs once when the cron daemon starts, so it has no five-field equivalent. Check that your platform supports macros before relying on them.
Ready-made examples
| Schedule | Expression |
|---|---|
| Every 5 minutes | */5 * * * * |
| Every hour at 15 minutes past | 15 * * * * |
| Every day at 03:00 | 0 3 * * * |
| Weekdays at 08:30 | 30 8 * * 1-5 |
| Every Monday at 06:00 | 0 6 * * 1 |
| First day of every month at 01:00 | 0 1 1 * * |
| Every 6 hours | 0 */6 * * * |
| Twice a day, 09:00 and 21:00 | 0 9,21 * * * |
| Quarterly, 1st of Jan, Apr, Jul, Oct at 04:00 | 0 4 1 1,4,7,10 * |
Rather than writing these by hand, you can build or paste one into our free cron expression generator, which explains it in plain English and lists the next run times.
The day-of-month and day-of-week trap
This is the rule that catches experienced developers. When both the day-of-month and day-of-week fields are restricted (neither is *), the job runs when either matches, not when both do.
The crontab manual's own example is 30 4 1,15 * 5, which runs at 04:30 on the 1st and 15th of each month and every Friday. If you wanted "the 1st of the month, but only if it is a Friday", standard cron cannot express it. The usual workaround is to schedule on one condition and check the other inside your script.
Time zones and daylight saving
Cron runs in a time zone, and it is often not the one you assume.
- Linux cron uses the system time zone unless you set
CRON_TZin the crontab, which the manual describes as setting the time zone for the table. - GitHub Actions says that "by default, scheduled workflows run in UTC", though it now lets you specify an IANA time zone for time zone-aware scheduling.
- Laravel lets you set a time zone per task or a
schedule_timezonefor all of them.
Daylight saving causes two problems. The crontab manual explains that times skipped when the clocks go forward never match, so those jobs do not run, and times repeated when the clocks go back make matching jobs run twice. Laravel's documentation gives the same warning and recommends "avoiding timezone scheduling when possible".
The safest habit is to schedule important jobs in UTC, or at local times well clear of the hour when your clocks change. Our time zone converter helps you translate a UTC schedule into local time for your team, while our guide to Unix timestamps and the timestamp converter help when you are reading job logs written in epoch time.
How different platforms use cron
Linux crontab
Edit your user's crontab with crontab -e, which, according to the crontab(1) manual, opens it in your configured editor and installs the changes when you exit. System crontabs such as /etc/crontab add a username field between the schedule and the command. Two practical points from the manual:
- An unescaped
%in the command is turned into a newline, and anything after it is sent to the command as input. Escape it as\%, which matters for commands likedate +\%F. - Output is emailed to the crontab owner, or to
MAILTOif set.MAILTO=""turns mail off.
Jobs do not necessarily get the same settings as your interactive login shell, so use full paths to programs and files rather than assuming your usual environment.
GitHub Actions
Scheduled workflows use POSIX cron syntax under on: schedule. GitHub's workflow trigger documentation adds some important limits:
- The shortest interval is once every 5 minutes.
- Scheduled runs can be delayed during periods of high load, and "some queued jobs may be dropped."
- Scheduled workflows only run on the default branch.
- In a public repository, schedules are automatically disabled after 60 days with no repository activity.
So do not use GitHub Actions for anything that must run at an exact minute. It is fine for jobs like our weekly automated broken link checks.
Laravel
Laravel's scheduler needs a single cron entry that runs every minute:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Your individual tasks are then defined in code, with methods such as ->daily(), ->weeklyOn(1, '8:00') or a raw ->cron('0 3 * * *'). Laravel adds withoutOverlapping() to stop a slow task from starting again before the last run finishes, and onOneServer() to stop a task running on every server when you have several.
Kubernetes
A Kubernetes CronJob uses the same five fields in .spec.schedule. Its concurrencyPolicy setting decides what happens when a run is still going at the next scheduled time (Allow, Forbid or Replace), and startingDeadlineSeconds controls how late a missed run may still start.
Common mistakes checklist
- Wrong time zone. Confirm whether your platform uses UTC, server time or a configured zone.
- Both day fields set. Remember the "either matches" rule.
- Steps that do not divide evenly, such as
*/7minutes or*/5hours. - Relative paths and missing environment variables in Linux cron.
- Unescaped
%in commands. - Overlapping runs. A job every 5 minutes that sometimes takes 10 needs a lock.
- No alert on failure. Cron will happily fail silently for months. Log output and alert on errors, or use a heartbeat service that warns you when a job stops checking in, as described in our guide to free website monitoring.
- Jobs at the daylight saving changeover hour in local time.
Scheduled jobs are the quiet backbone of most automation, from backups to reports. If you would rather not maintain a pile of cron entries yourself, Make It Flow helps teams design scheduled workflows with logging and alerts built in.
Frequently asked questions
What does * * * * * mean in cron?
Five asterisks mean every minute of every hour of every day of every month, on every day of the week. It is the most frequent schedule standard cron supports. Laravel uses exactly this entry to run its scheduler, which then decides which tasks are due.
How do I run a cron job every 30 seconds?
Standard cron cannot go below one minute. Options include running a script every minute that does its work twice with a 30-second pause, using a framework scheduler that supports sub-minute tasks, as Laravel does, or using a long-running worker or queue instead of cron.
Is Sunday 0 or 7 in cron?
Both, in standard Linux cron: the crontab manual says 0 or 7 is Sunday. Some platforms only accept 0–6; the Kubernetes documentation, for example, lists 0–6 for Sunday to Saturday. Using the three-letter names such as sun avoids the question where they are supported.
Why didn't my cron job run?
Common causes are a different time zone than expected, a daylight saving skip, a command that works in your shell but fails in cron's minimal environment, an unescaped percent sign, or on GitHub Actions, delays under load, a schedule on a non-default branch or a public repository that has been inactive for 60 days.
What is the difference between cron and crontab?
Cron is the background service that runs scheduled jobs. A crontab, short for cron table, is the file listing those jobs and their schedules. You edit your crontab with crontab -e, and the cron daemon reads it and runs each job when its expression matches the current time.
Related reading
- Unix Timestamp Explained: Epoch Time, Milliseconds, BST and the 2038 Problem: A Unix timestamp counts seconds since 1 January 1970 UTC. Learn seconds vs milliseconds, the 2038 problem, handling BST and time zones, and ISO 8601 dates.
- Automate Broken Link Checks: Scheduled Crawls and GitHub Actions: How to run broken link checks automatically with lychee or linkinator on a GitHub Actions schedule, check external links politely and fix them in bulk.
- Automate Website Monitoring for Free: Uptime, Audits and Alerts: Set up free automated website monitoring: uptime checks with UptimeRobot, Better Stack or Uptime Kuma, scheduled audits, sensible alert rules and less noise.
Sources
More guides for your industry
Free checklist (PDF)
Free software & saas website checklist
31 practical checks for software & saas: website speed and mobile, Google Business Profile and reviews, email security, booking and enquiry handling, and AI search visibility. Each one says why it matters, and many link to a free tool that checks it.
Written by the MakeItFlow team
We build the website audit these guides are based on. Every fact is checked against the primary sources listed above and this guide was last reviewed on 12 November 2026 . How we research and check
Check your own site in 30 seconds
Run a free audit covering speed, SEO, DNS, email authentication and security headers — and get a prioritised fix list.