Quick Start Guide to Cron Syntax

Everything you need to know to write reliable cron expressions — from first principles to production best practices.

What is a Cron Job?

A cron job is a time-based task scheduler found in Unix-like operating systems and modern cloud platforms. The name comes from the Greek word "chronos" (time), and the daemon — crond — has been running background tasks on servers since the 1970s. Today, cron-style scheduling powers everything from simple backup scripts on a single VPS to complex distributed workflows orchestrated across multi-region cloud infrastructure.

At its core, a cron job consists of two parts: a schedule expression that defines when the task runs, and a command or target that defines what happens. The schedule expression — commonly called a "cron expression" — is a compact string of fields that encodes timing information in a machine-readable format. Learning to read and write these expressions is a foundational skill for any developer working with automation, CI/CD pipelines, or cloud infrastructure.

Modern platforms have extended the original Unix syntax to support more granular scheduling. AWS EventBridge adds a year field and uses question marks. Quartz (used in Java/Spring applications) adds a seconds field and special modifiers like L (last), W (weekday), and # (nth occurrence). Kubernetes CronJobs use standard 5-field Unix cron but add concurrency policies and deadline controls. Understanding these differences is critical for deploying reliable schedules across heterogeneous environments.

Understanding the 5 Fields

A standard Unix cron expression contains exactly five fields, separated by spaces. Each field controls one dimension of timing. Together, they answer the question: "At which minute, of which hour, on which day(s), in which month(s), and on which weekday(s) should this job execute?"

MinuteHourDay of MonthMonthDay of Week

Minute (0–59): Which minute of the hour the job fires. A value of 0 means the top of the hour; */5 means every five minutes.

Hour (0–23): Uses 24-hour time.9 is 9 AM, 17 is 5 PM. Ranges like 9-17 restrict execution to business hours.

Day of Month (1–31): Which calendar day. Be careful with values above 28 — not every month has 31 days. Use L in Quartz for "last day" to avoid edge cases.

Month (1–12 or JAN–DEC): Which month(s) of the year. Supports numbers, three-letter abbreviations, ranges, and step values.

Day of Week (0–7 or SUN–SAT): Both 0 and 7 represent Sunday in Unix cron. Quartz uses 1=Sunday through 7=Saturday. This difference is a common source of bugs when migrating between platforms.

Special characters expand expression power: * (any value), , (list), - (range), / (step). Mastering these four operators lets you express virtually any repeating schedule.

Why Timezones Matter

Cron expressions don't inherently carry timezone information — the system or platform they run on determines the reference clock. This creates a class of subtle bugs that only manifest twice a year: during Daylight Saving Time transitions. A job scheduled for 2:30 AM might execute twice on "fall back" night, or be skipped entirely on "spring forward" night.

Unix crontab uses the system's configured timezone (typically set in /etc/timezone). If your server is in UTC but your business operates in US/Eastern, every schedule needs manual offset calculation.

AWS EventBridge always evaluates cron expressions in UTC. To schedule a job at 9 AM Eastern, you must account for the UTC offset — and remember it changes between EST (-5) and EDT (-4).

Kubernetes CronJobs default to the kube-controller-manager's timezone (usually UTC) but support the timeZone field since v1.27 (stable). Always set this explicitly to avoid surprises after cluster upgrades.

Best practice: Always define schedules in UTC and convert for display, or explicitly configure the timezone on platforms that support it. Never assume the system clock matches your users' expectations. Our generator shows execution times in your local timezone precisely to help you catch these mismatches before they hit production.

Cron Expression Generator

Build, validate, and understand cron expressions with our visual builder. Supports Unix, Quartz, AWS EventBridge, Spring Boot, and Azure Functions dialects.

Visual Builder

Linux/Unix — 5 fields
[0–59]
*
[0–23]
*
[1–31]
*
[1–12]
*
[0–6]
*
Quick Presets
Common
Daily
Business
Weekly
Monthly
Expression
* * * * *
* = minute* = hour* = dayOfMonth* = month* = dayOfWeek
Plain English

Every minute

⌘+Shift+Dto copy description

Valid Linux/Unix cron expression
Next 5 Executions
1
Wed, Aug 05 202621:25:00
in 10 seconds
2
Wed, Aug 05 202621:26:00
in 1 minute
3
Wed, Aug 05 202621:27:00
in 2 minutes
4
Wed, Aug 05 202621:28:00
in 3 minutes
5
Wed, Aug 05 202621:29:00
in 4 minutes

Learn More About Cron Expressions

A cron expression is a compact, whitespace-delimited string that defines a recurring schedule. Originally designed for the Unix cron daemon in the 1970s, the format has evolved into multiple dialects used across cloud platforms, container orchestrators, and application frameworks. Understanding the difference between field formats is critical to avoiding silent scheduling failures in production.

Standard 5-Field Format (Unix/Linux crontab)

The classic cron format uses exactly five fields, each separated by a single space. This is the format used bycrontab -e on Linux/macOS, Kubernetes CronJobs, GitHub Actions schedules, and most CI/CD pipelines.

5-field format
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12 or JAN–DEC)
│ │ │ │ ┌───────────── day of week (0–6 or SUN–SAT)
│ │ │ │ │
* * * * *

6-Field Format (AWS EventBridge, Spring, Azure)

Many modern platforms extend the standard with an additional field. AWS EventBridge adds a year field at the end, while Spring Boot's @Scheduled and Azure Functions Timer Triggers prepend a seconds field. Confusing these formats is the #1 cause of "my cron job runs at the wrong time" bugs.

AWS EventBridge (6-field with year)
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12 or JAN–DEC)
│ │ │ │ ┌───────────── day of week (SUN–SAT or 1–7)
│ │ │ │ │ ┌───────────── year (1970–2199)
│ │ │ │ │ │
* * * * ? *
Spring / Azure (6-field with seconds)
┌───────────── second (0–59)
│ ┌───────────── minute (0–59)
│ │ ┌───────────── hour (0–23)
│ │ │ ┌───────────── day of month (1–31)
│ │ │ │ ┌───────────── month (1–12 or JAN–DEC)
│ │ │ │ │ ┌───────────── day of week (0–6 or SUN–SAT)
│ │ │ │ │ │
0 * * * * *

7-Field Format (Quartz Scheduler)

Java's Quartz Scheduler uses the most extended format with 7 fields: seconds, minutes, hours, day-of-month, month, day-of-week, and an optional year. Quartz also introduces the ? character which means "no specific value" — required in either day-of-month or day-of-week (you must use ? in one of them).

Key Differences at a Glance

FeatureUnixAWSQuartzSpring/Azure
Fields566–76
SecondsNoNoYesYes
YearNoYesOptionalNo
? characterNoYesRequiredYes
L/W/# charsNoL, WL, W, #No
DOW numbering0=Sun1=Sun1=Sun0=Sun
☁️

AWS EventBridge / CloudWatch Events

AWS EventBridge uses a 6-field cron format with a mandatory year field. All times are evaluated in UTC only — there is no timezone parameter. You must use ? in either day-of-month or day-of-week (not both can be *).

EventBridge rule — Every weekday at 9:00 AM UTC
cron(0 9 ? * MON-FRI *)

# In AWS CLI:
aws events put-rule \
  --name "weekday-morning" \
  --schedule-expression "cron(0 9 ? * MON-FRI *)" \
  --state ENABLED

Key differences from Unix cron: Days of week are 1-7 (SUN=1) instead of 0-6. The year field supports 1970–2199. AWS supports L (last) and W (nearest weekday) in the day-of-month field.

⚠️ EventBridge has a minimum resolution of 1 minute. Sub-minute scheduling requires Step Functions or Lambda with self-invocation.

Kubernetes CronJobs

Kubernetes CronJobs use the standard 5-field Unix cron format. The schedule is evaluated against the timezone of the kube-controller-manager. Since Kubernetes v1.25+, you can set .spec.timeZone explicitly.

CronJob manifest — Daily backup at 2:30 AM EST
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "30 2 * * *"
  timeZone: "America/New_York"  # K8s 1.25+
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: myregistry/backup:latest
            command: ["/bin/sh", "-c", "./backup.sh"]
          restartPolicy: OnFailure

Best practices: Always setconcurrencyPolicy to prevent overlapping jobs. Use startingDeadlineSeconds to handle missed schedules gracefully. Monitor with kubectl get cronjobs.

💡 Tip: Use kubectl create cronjob test --image=busybox --schedule="*/5 * * * *" --dry-run=client -o yaml to generate a manifest quickly.
🔷

Azure Functions Timer Trigger

Azure Functions uses a 6-field NCRONTAB format with seconds as the first field. By default, timers run in UTC. Set the WEBSITE_TIME_ZONE app setting to use a specific timezone (Windows timezone IDs on Windows hosts, IANA on Linux).

Azure Timer Trigger — Every 30 seconds
// function.json
{
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "*/30 * * * * *"
    }
  ]
}

// C# attribute syntax:
[Function("TimerFunction")]
public void Run(
    [TimerTrigger("0 */5 * * * *")] TimerInfo timer)
{
    // Runs every 5 minutes at second 0
}

Azure-specific features: Supports%schedule% syntax to reference app settings. The RunOnStartup property triggers execution when the function app starts. Use UseMonitor to prevent missed schedule catch-ups.

📘 Azure's Consumption plan may have cold-start delays. For time-critical jobs, use Premium or Dedicated plans.

1Confusing field order between platforms

0 0 9 * * 1-5 (intended: "weekdays at 9 AM" in Unix format)
0 9 * * 1-5 (Unix: 5 fields only — no seconds field)

A common mistake is prepending "0" for seconds in a 5-field Unix cron, accidentally shifting all fields. The "0" becomes the minute, "9" becomes the hour incorrectly, etc.

2Using * in both day-of-month and day-of-week (AWS/Quartz)

cron(0 9 * * * *) — AWS EventBridge rejects this
cron(0 9 ? * * *) or cron(0 9 * * ? *)

In AWS and Quartz, you must use "?" in either DOW or DOM. Using "*" in both is ambiguous and will fail validation.

3Day-of-week numbering differences

0 9 * * 7 (expecting Sunday in AWS — but 7 is SAT in some systems)
0 9 * * SUN (use names for clarity across platforms)

Unix uses 0=Sunday (with 7 as an alias for Sunday in some implementations). AWS uses 1=Sunday through 7=Saturday. Always use SUN-SAT names to avoid confusion.

4Forgetting UTC-only evaluation in AWS

Scheduling "9 AM local" without timezone conversion
Convert to UTC first: 9 AM EST = cron(0 14 ? * * *) in winter

AWS EventBridge evaluates all cron expressions in UTC. You must manually convert your local time to UTC, and account for DST changes twice a year.

5Using step values with wrong base

*/3 9-17 * * * (expecting every 3 min only during business hours)
0/3 9-17 * * * (same result, but explicit start at 0)

While */3 works the same as 0/3 for minutes (both start at 0), using explicit start values like 5/15 (starting at minute 5) gives you more control. The gotcha is that */3 in hour field means 0,3,6,9,12,15,18,21 — not "every 3 hours starting from the current hour".

6Overlapping time windows in Kubernetes CronJobs

Running a 10-minute job with "*/5 * * * *" schedule
Set concurrencyPolicy: Forbid and startingDeadlineSeconds

If your job takes longer than the interval between executions, Kubernetes will start overlapping instances. Use concurrencyPolicy: Forbid to skip new runs while one is active, or Replace to kill and restart.

7Month and weekday ranges with step values

0 0 * 1-12/3 * (expecting quarterly — Jan, Apr, Jul, Oct)
0 0 1 1,4,7,10 * (explicit quarterly months)

While 1-12/3 does mean months 1,4,7,10, it is easier to misread. More importantly, this pattern fails for edge cases: 1-6/4 gives only months 1 and 5 (not 4 values). Be explicit with lists for clarity.

8Azure Timer Trigger cold starts

Expecting sub-second precision from "*/1 * * * * *" on Consumption plan
Use Premium plan for time-sensitive jobs or add UseMonitor=true

Azure Functions on the Consumption plan can have cold starts of 1-10+ seconds. Timer triggers may fire late after the app wakes from idle. Use Premium/Dedicated plans or the RunOnStartup property for critical schedules.

Under the Hood

How our engine unifies five distinct scheduling syntaxes into one accurate, real-time interface.

Multi-Dialect Regex Parser

Each platform has unique syntax rules. Our parser uses dialect-specific regex patterns to validate and tokenize expressions:

// Unix: 5 fields (min hr dom mon dow)
unix: /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/
// Quartz: 6-7 fields (sec min hr dom mon dow [yr])
quartz: /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)(\s+\S+)?$/
// AWS: 6 fields with year, rate() and cron()
aws: /^cron\((.+)\)$|^rate\((.+)\)$/

Cross-Platform Field Normalization

Platforms disagree on fundamentals. Our normalization layer handles the conflicts transparently:

  • Day-of-week numbering: Unix uses 0-6 (Sun=0), Quartz uses 1-7 (Sun=1), AWS uses 1-7 (Sun=1)
  • Mutual exclusion: Quartz requires ? in either DOM or DOW — never both with values
  • Year field: AWS and Quartz support it; Unix and Azure do not
  • Seconds precision: Quartz and Azure include seconds; Unix and AWS do not

Platform-Aware Validation

Validation isn't one-size-fits-all. Each dialect has specific rules that our engine enforces:

QuartzValidates L (last), W (weekday), # (nth) modifiers
AWSEnforces rate() unit constraints (minutes ≥ 1)
AzureValidates NCRONTAB 6-field format with seconds
K8sStandard 5-field with ≥1 minute granularity

Timezone-Aware Run Calculation

Computing "next runs" correctly requires handling edge cases most tools ignore:

  • DST transitions: A 2:30 AM job during spring-forward doesn't fire — we detect and warn about this
  • Month boundaries: "31st of every month" correctly skips months with fewer days
  • Leap years: Feb 29 schedules are validated against the actual calendar
  • Efficient iteration: Uses field-jumping instead of brute-force minute scanning for fast previews

Processing Pipeline

User Input
Dialect Detection
Regex Tokenization
Platform Validation
Field Normalization
Schedule Computation

All processing happens client-side in your browser — zero server round-trips, instant feedback.