Uptimehub
FREE TOOL - BUILD, EXPLAIN AND PREVIEW A SCHEDULE

Cron Expression Generator: Crontab Generator and Cron Schedule Explainer

Pick a schedule and get the expression, or paste an expression and get it back in plain English with its next run times. Covers Unix crontab, Kubernetes, Quartz, Spring, AWS EventBridge and Azure NCRONTAB, including the weekday numbering that differs between them.

Runs in your browser Nothing stored Updated August 2026

Cron expression generator

Live

minute hour day-of-month month day-of-week

Build a common schedule

What a cron expression is

A cron expression is a string of five to seven fields that tells a scheduler when to run a job. Reading left to right, the fields are minute, hour, day of month, month and day of week, with an optional leading seconds field and an optional trailing year field in some dialects. The generator above builds the expression from a schedule you describe, and reads any expression back in plain English with the next run times, so you can confirm a schedule means what you think before it goes into production.

The reason a generator is worth using rather than writing the string by hand is that cron syntax gives you no feedback. A wrong expression is usually still a valid expression, so nothing errors and nothing warns. It just runs at a different time than you meant, and you find out weeks later when a report arrives on the wrong day or a nightly job turns out to have been running hourly.

That is also why the tool above shows the next run times rather than only the description. Reading a schedule back as a sentence catches most mistakes. Seeing the actual dates and times it will fire catches the rest, including the ones caused by the month having no 31st day or the weekday numbering differing from the system you copied the expression from.

The five field form, left to right

* * * * *
| | | | |
| | | | +--- day of week  (0 to 7)
| | | +----- month        (1 to 12)
| | +------- day of month (1 to 31)
| +--------- hour         (0 to 23)
+----------- minute       (0 to 59)

Every dialect on this page is a variation on those five fields. Some add seconds at the front, some add a year at the end, and they disagree about how to number the days of the week.

Field Allowed values Present in What to watch
Seconds 0 to 59 Optional, and only in Quartz, Spring and Azure NCRONTAB Leading field when present. Unix cron and Kubernetes have no seconds field at all, so one minute is their finest interval.
Minutes 0 to 59 Always present The first field in Unix cron, the second field in any dialect that carries seconds.
Hours 0 to 23 Always present A 24 hour clock. There is no AM or PM notation and no 12 hour form in any dialect.
Day of month 1 to 31 Always present Interacts with day of week in a way that surprises almost everyone. See the section on the OR rule below.
Month 1 to 12 or JAN to DEC Always present Names are case insensitive and are the safer choice, since 1 is January everywhere but weekday numbering is not consistent.
Day of week 0 to 7 or 1 to 7, depending on the dialect Always present The field that causes the most production incidents. The number that means Monday in Unix cron means Tuesday in Quartz.
Year 1970 to 2099 or 2199 Optional, and only in Quartz and AWS EventBridge Trailing field. Rarely used outside one off scheduled tasks and backfills.
// SIX DIALECTS COMPARED

Read from each vendor's own documentation

Cron expression syntax is not one standard, it is six

Most cron generators assume Unix syntax and stop there. In practice a schedule written on a Linux box gets pasted into a Kubernetes manifest, a Spring annotation, a Quartz trigger, an EventBridge rule or an Azure timer, and four of those five read at least one field differently. The table below is a plain reading of the primary documentation for each, checked in August 2026.

Scheduler Fields Order Seconds Day of week Sunday is Both day fields at once Default time zone
Unix / Linux crontab 5 min hour dom mon dow No 0 to 7 0 and 7 both Yes, OR matched System local time
Kubernetes CronJob 5 min hour dom mon dow No 0 to 6 0 Yes, OR matched Controller local time, or .spec.timeZone
Spring @Scheduled 6 sec min hour dom mon dow Yes 0 to 7 0 and 7 both Yes, OR matched Server default, or the zone attribute
Quartz (Java and .NET) 6 or 7 sec min hour dom mon dow [year] Yes 1 to 7 1 No, one must be ? Trigger time zone, defaulting to the JVM
AWS EventBridge 6 min hour dom mon dow year No 1 to 7 1 No, one must be ? UTC, always
Azure Functions NCRONTAB 6 (5 also accepted) sec min hour dom mon dow Yes 0 to 6 0 Yes, OR matched UTC, or WEBSITE_TIME_ZONE

The off by one that has no error message

Unix cron numbers Sunday as 0. Quartz and EventBridge number Sunday as 1. So the same day of week digit points at a different day in the two families, and every value stays inside both valid ranges. The job keeps running, one day early, and no log line anywhere says why.

Names are portable, numbers are not

MON means Monday in all six dialects. The digit 1 means Monday in four of them and Sunday in the other two. Writing SUN to SAT instead of 0 to 7 removes the entire class of bug at zero cost, which is why every recipe in the table below uses names wherever a weekday is involved.

Two of the six always run on UTC

AWS EventBridge scheduled rules run on UTC with no option, and Azure Functions default to UTC unless WEBSITE_TIME_ZONE is set. Unix cron uses the machine time zone. The same expression therefore fires at different local times depending only on where it was deployed.

Sources: the crontab(5) man page, Kubernetes CronJob documentation, Spring CronExpression javadoc, the Quartz CronTrigger tutorial, AWS EventBridge schedule pattern documentation and the Azure Functions timer trigger reference. All re-read August 2026.

// COMMON CRON SCHEDULES

Copy and paste

Cron expressions for every 5 minutes, hourly, daily and weekly

The schedules people actually need, written out for the three field counts. The Unix column also applies to Kubernetes CronJobs. The Quartz column also applies to Spring, except that Spring uses * rather than ? in the day fields.

Schedule Unix cron and Kubernetes Quartz (6 fields) AWS EventBridge
Every minute * * * * * 0 * * * * ? * * * * ? *
Every 5 minutes */5 * * * * 0 */5 * * * ? */5 * * * ? *
Every 10 minutes */10 * * * * 0 */10 * * * ? */10 * * * ? *
Every 15 minutes */15 * * * * 0 */15 * * * ? */15 * * * ? *
Every 30 minutes */30 * * * * 0 */30 * * * ? */30 * * * ? *
Every hour, on the hour 0 * * * * 0 0 * * * ? 0 * * * ? *
Every 2 hours 0 */2 * * * 0 0 */2 * * ? 0 */2 * * ? *
Every 6 hours 0 */6 * * * 0 0 */6 * * ? 0 */6 * * ? *
Every day at midnight 0 0 * * * 0 0 0 * * ? 0 0 * * ? *
Every day at 02:30 30 2 * * * 0 30 2 * * ? 30 2 * * ? *
Weekdays at 09:00 0 9 * * MON-FRI 0 0 9 ? * MON-FRI 0 9 ? * MON-FRI *
Every Monday at 09:00 0 9 * * MON 0 0 9 ? * MON 0 9 ? * MON *
Every Sunday at 03:00 0 3 * * SUN 0 0 3 ? * SUN 0 3 ? * SUN *
First day of the month 0 0 1 * * 0 0 0 1 * ? 0 0 1 * ? *
Last day of the month Not expressible, use a guard 0 0 0 L * ? 0 0 L * ? *
Every quarter 0 0 1 1,4,7,10 * 0 0 0 1 1,4,7,10 ? 0 0 1 1,4,7,10 ? *

What the step operator actually does

The most common misreading of cron is treating */5 as "five minutes after the last run". It is not an interval timer. The slash defines which values in the field match, so */5 in the minute field matches minutes 0, 5, 10 and so on up to 55. If a run starts at 12:04:59 and takes two minutes, the next one still fires at 12:10, not at 12:07.

A bare value before the slash shifts the starting point instead of using the bottom of the range. In AWS and Quartz, 5/15 in the minute field means 5, 20, 35 and 50. That is the tool of choice for spreading load: five jobs written as 0/15, 3/15, 6/15, 9/15 and 12/15 all run four times an hour without ever landing on the same minute.

Cron also never protects you from overlap. If a job scheduled every 5 minutes takes 7 minutes, cron starts the next copy anyway, and by the end of the day you have several instances competing for the same rows. Any job that can run long needs its own lock, whether that is a lock file, an advisory lock in the database, or a queue that refuses duplicate work.

Macros, and where they work

Macro Equivalent
@yearly / @annually0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily / @midnight0 0 * * *
@hourly0 * * * *

Unix cron, Kubernetes and Spring accept these. AWS EventBridge and Azure NCRONTAB do not, so write the schedule out in full for those. There is also @reboot in Unix cron, which is not a schedule at all: it runs once when cron starts, and it is the wrong tool for anything that needs to happen reliably.

// SPECIAL CHARACTERS

Operators

Every cron special character, and which schedulers accept it

Character Means Supported by Example
* All values Every dialect Matches every value the field allows. An hour field of * means every hour.
, List separator Every dialect MON,WED,FRI in the day of week field means those three days and no others.
- Range Every dialect 9-17 in the hour field covers every hour from 09:00 through 17:00 inclusive.
/ Step Every dialect */15 in the minute field means minute 0, 15, 30 and 45. Written as 5/15 it starts at 5 instead, giving 5, 20, 35 and 50.
? No specific value Quartz and AWS EventBridge only Means "I am not using this field". Required in whichever of day of month or day of week you are not constraining. Unix cron rejects it.
L Last Quartz and AWS EventBridge L in day of month is the last day of the month. 6L in day of week is the last Friday of the month.
W Nearest weekday Quartz and AWS EventBridge 15W means the weekday closest to the 15th, which is how payroll and invoicing schedules avoid landing on a Saturday.
# Nth weekday of the month Quartz and AWS EventBridge 6#3 is the third Friday of the month. Only one # expression is allowed in the field.
H Hash, spread the load Jenkins only Jenkins replaces H with a value derived from the job name, so a hundred jobs set to H * * * * do not all fire on the same second.

The generator above computes run times for the portable operators. Where an expression uses L, W or #, it tells you the syntax is valid for that dialect but declines to guess the dates, because those operators depend on calendar rules that differ between implementations and a confident wrong answer is worse than no answer.

// SIX WAYS CRON SURPRISES YOU

Why a valid expression runs at the wrong time

The cron mistakes that reach production

Every one of these produces a perfectly valid expression. That is what makes them expensive: there is no syntax error, no warning at deploy time, and no log line. The schedule simply is not the schedule you meant.

The weekday number means a different day in a different scheduler

This is the single most expensive cron mistake. Unix cron, Kubernetes, Spring and Azure start the week at 0 for Sunday. Quartz and AWS EventBridge start at 1 for Sunday. So 0 9 * * 5 runs on Friday in a Linux crontab, but the same 5 in a Quartz or EventBridge day of week field means Thursday. A schedule copied from a server into a Java application or an AWS rule keeps running, one day earlier, and nothing errors. Write MON to SUN names instead of numbers and the problem disappears.

Both day fields set, so the job runs far more often than intended

The crontab man page is explicit: "If both fields are restricted (i.e., do not contain the * character), the command will be run when either field matches the current time." So 0 0 1 * MON is not "the first of the month, if it is a Monday". It is "the first of the month, and also every Monday", which is roughly five times more runs than intended. Quartz and EventBridge sidestep this by refusing the combination outright and requiring a ? in one field.

The schedule is in a time zone nobody checked

EventBridge scheduled rules always run on UTC. Azure Functions default to UTC. Unix cron uses the machine time zone. Kubernetes uses the controller time zone unless .spec.timeZone is set. A schedule that reads 0 2 * * * looks like a quiet 2am maintenance window and can land at 9pm or 10pm local, in the middle of peak traffic, depending on where it runs.

Daylight saving quietly skips or repeats a run

On the spring forward date, a job scheduled for 02:30 local simply does not run in most implementations, because 02:30 never happens. On the autumn date, 02:30 happens twice, and some schedulers run the job twice. Anything doing billing, reconciliation or reporting should either run in UTC or be safe to run twice.

Day 31 silently skips five months a year

A schedule of 0 0 31 * * runs in January, March, May, July, August, October and December only. The other five months never have a 31st. The same trap catches 29 February, which exists once every four years. Use L in Quartz or EventBridge, or schedule on the 1st and subtract a day in code.

The environment inside cron is not the shell you tested in

Not strictly an expression problem, but it is the most common reason a correct expression appears not to work. Cron runs with a minimal PATH and almost none of your environment, so a script that runs perfectly by hand fails silently under cron. Use absolute paths, and remember that an unescaped % in the command is turned into a newline by cron rather than passed through.

// FROM SCHEDULING TO KNOWING

The schedule is only half the problem

A cron expression says when a job should run, not whether it did

Getting the expression right is the easy half. The hard half is that cron has no concept of success. It starts a process at the appointed time and forgets about it. If the process exits with an error, if the machine was rebooting, if the container had been evicted, if the disk was full, if a deploy removed the crontab entry three weeks ago, cron reports nothing at all.

This is why scheduled jobs fail differently from web services. A website going down produces complaints within minutes. A nightly backup that has silently not run since March produces nothing until the day somebody needs the backup. The absence of a signal looks exactly like everything being fine.

Heartbeat monitoring inverts the check. Instead of watching the job, you have the job tell you it finished, and the monitor alerts when that message does not arrive inside the window you expect. The schedule you build above becomes the expected interval, and silence becomes the alert rather than the default. Our guide to why a cron job does not run at its scheduled time walks through the causes in order of how often they turn out to be the answer.

What a heartbeat check adds

  • An alert when a job does not report in, which is the failure mode cron itself cannot detect
  • A grace period, so a job that usually takes four minutes and occasionally takes nine does not page anybody
  • A record of every run, so "when did this last succeed" has an answer that is not a guess
  • The same alert path as your HTTP, API, SSL, DNS and TCP port checks, rather than a separate system
  • Coverage of the jobs that matter most: backups, billing runs, reconciliation and data exports
// CRON EXPRESSION FAQ

Questions

Cron expression questions, answered

What is a cron expression?

A cron expression is a string of five to seven space separated fields that defines a repeating schedule. The five field form is minute, hour, day of month, month and day of week, so 0 9 * * MON-FRI means 09:00 on every weekday. Some schedulers add a leading seconds field, and Quartz and AWS EventBridge add an optional trailing year field.

What is the cron expression for every 5 minutes?

In Unix cron and Kubernetes it is */5 * * * *. In Quartz and Spring, which carry a seconds field, it is 0 */5 * * * ? and 0 */5 * * * * respectively. In AWS EventBridge it is */5 * * * ? *. The step operator */5 means minute 0, 5, 10 and so on through 55, not "five minutes after whenever the job last ran".

How do I write a cron expression for every hour?

Use 0 * * * * in Unix cron, which means minute 0 of every hour. A common mistake is writing * * * * *, which runs every minute of every hour, sixty times more often than intended. The @hourly macro is equivalent to 0 * * * * and is accepted by Unix cron, Kubernetes and Spring.

What does the question mark mean in a cron expression?

The ? character means "no specific value" and exists only in Quartz and AWS EventBridge. Those two schedulers refuse to read the day of month and day of week fields at the same time, so you put a real value in the one you care about and ? in the other. Unix cron has no ? and rejects the expression outright.

Is 0 Sunday or Monday in cron?

In Unix cron, Kubernetes, Spring and Azure NCRONTAB, 0 is Sunday, and Unix cron and Spring also accept 7 as Sunday. In Quartz and AWS EventBridge there is no 0 at all: the range is 1 to 7 and 1 is Sunday. That single off by one is why a schedule copied between systems fires a day early without any error being raised.

Can a cron expression run every 30 seconds?

Not in Unix cron or Kubernetes, which have no seconds field and cannot express anything finer than one minute. Quartz, Spring and Azure NCRONTAB do have a seconds field, so 0/30 * * * * ? works in Quartz. On Linux the usual workarounds are a systemd timer, or two crontab entries where the second one sleeps 30 seconds first.

What is the difference between crontab and a cron expression?

The cron expression is the schedule itself, the five fields that say when. The crontab is the file that holds a list of those expressions, each paired with the command to run, for a particular user. You edit it with crontab -e and list it with crontab -l. A generator produces the expression; you still paste it into the crontab with the command beside it.

Why did my cron job not run even though the expression is correct?

Almost always the environment rather than the schedule. Cron runs with a minimal PATH, no shell profile and a different working directory, so scripts that work interactively fail under cron. Other frequent causes are a missing execute bit, no trailing newline in the crontab file, output going nowhere because no mail transfer agent is installed, and the machine being asleep or the container not running at the scheduled moment.

Know that the job ran, not just that it was scheduled

Cron heartbeat checks alert you when a scheduled job stops reporting in, alongside HTTP, keyword, API, SSL, DNS and TCP port checks from six regions on one alert path. From $9 a month, priced per monitor and never per seat.