How to Build Cron Expressions

· 9 min read

Cron expressions are the standard way to define recurring schedules in Linux, cloud platforms, CI/CD pipelines, and task schedulers. The syntax is compact but not intuitive, building a visual cron generator shows you exactly when your job will run, catches common errors before deployment, and removes the guesswork from the most error-prone part of automation. Once you understand the five fields, the special characters, and the most common pitfalls, you can specify any recurring schedule with confidence.

A short history of cron

The first cron came from Brian Kernighan in Version 7 Unix, around 1979. It re-read its configuration every minute and ran whatever was due. Paul Vixie rewrote it in 1987 into what is now called Vixie cron, which is the version most Linux distributions still ship. Vixie cron added per-user crontabs, environment variables, the @reboot keyword, and several quality-of-life features that made the format usable for non-administrators.

The 5-field syntax has barely changed in over forty years. Amazon EventBridge, Google Cloud Scheduler, Kubernetes CronJob, GitHub Actions, GitLab CI, Jenkins, Airflow, n8n, and dozens of other systems all consume the same compact format with only minor extensions. That stability is the reason cron is worth learning once and then never re-learning. The skill transfers everywhere automation runs on a clock.

Cron syntax

A standard cron expression has 5 fields, separated by spaces. Each field controls one slice of time, and a job runs whenever every field matches the current moment.

┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12, or JAN-DEC)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0, or SUN-SAT)
│ │ │ │ │
* * * * *

The fields are ANDed together for hour, minute, and month, but day-of-month and day-of-week are ORed in Vixie cron. That means 0 12 1 * 1 fires on both the 1st of every month AND every Monday at noon, not only on Mondays that happen to be the 1st. This trap catches almost everyone the first time.

Common cron schedules

The patterns you will reach for most often:

Schedule Expression Meaning
Every minute * * * * * Runs every 60 seconds
Every 5 minutes */5 * * * * At :00, :05, :10, :15...
Every 15 minutes */15 * * * * At :00, :15, :30, :45
Every hour 0 * * * * At the top of every hour
Every 2 hours 0 */2 * * * At 00:00, 02:00, 04:00...
Daily at midnight 0 0 * * * Once per day at 00:00
Daily at 9 AM 0 9 * * * Once per day at 09:00
Twice a day 0 9,21 * * * At 09:00 and 21:00
Every Monday at 8 AM 0 8 * * 1 Weekly on Monday
Weekdays at 6 PM 0 18 * * 1-5 Monday through Friday
First of every month 0 0 1 * * Monthly at midnight on the 1st
Every quarter 0 0 1 */3 * Jan 1, Apr 1, Jul 1, Oct 1
Every weekday morning 0 7 * * 1-5 07:00 Mon-Fri
Sunday at noon 0 12 * * 0 Weekly on Sunday

Many systems also accept shorthand aliases that expand to the equivalent 5-field expression: @yearly, @monthly, @weekly, @daily, @hourly, and @reboot. They are concise but not universal, so check your platform before relying on them.

How to build a cron expression

  1. Pick the granularity: do you need every minute, every hour, once a day, once a week, or once a month? Start at the coarsest setting that meets your need.
  2. Use the visual controls: select minute, hour, day, month, and weekday values from the dropdowns. Or start with a preset like "every hour" or "daily at midnight" and adjust.
  3. Preview the next run times: the generator shows the next 5 execution times, which lets you confirm the schedule fires when you expect.
  4. Sanity-check the timezone: the preview should match the timezone of the server or scheduler that will run the job, not your local time.
  5. Copy the expression and paste it into your crontab, GitHub Actions YAML, AWS EventBridge rule, or whichever scheduler you use.
  6. Test with a short interval first before committing the final schedule. A quick */5 * * * * proves the job fires; once you see two or three runs land, swap in the real expression.

Special characters and operators

Cron supports a small but powerful set of operators inside any field.

Character Meaning Example
* Every value * * * * * = every minute
*/n Every nth */15 * * * * = every 15 min
, Multiple discrete values 0 8,12,18 * * * = 8am, noon, 6pm
- Inclusive range 0 9-17 * * * = every hour 9am-5pm
n-m/k Range with step 0 9-17/2 * * * = 9, 11, 13, 15, 17
? No specific value (Quartz only) 0 0 ? * MON (Java schedulers)
L Last (AWS, Quartz) L in DoM = last day of the month
W Nearest weekday (AWS, Quartz) 15W = weekday nearest the 15th
# Nth weekday (AWS, Quartz) MON#2 = second Monday of the month
@hourly Shorthand Same as 0 * * * *

Vanilla Vixie cron only supports the first five rows. The advanced operators (L, W, #, ?) come from Quartz, the Java scheduling library, and were adopted by AWS EventBridge and a few other cloud schedulers. They are not portable, so do not mix them with code that has to run on a generic Linux box.

Cron on different platforms

Cron is a family of related syntaxes, not a single standard. Knowing which dialect your scheduler speaks saves hours of debugging.

Platform Fields Notes
Vixie cron (Linux) 5 The classic. */n, ranges, lists, no advanced operators
BSD cron 5 Like Vixie but slight environment differences
crontab.guru 5 Web-based parser that mirrors Vixie semantics
GitHub Actions 5 Vixie syntax, runs in UTC, at least 5 minute resolution
GitLab CI 5 Vixie syntax, runs in instance timezone
AWS EventBridge 6 Adds year. Day-of-week uses 1-7 (Sun=1), supports L/W/#
Google Cloud Scheduler 5 Vixie syntax plus timezone configuration
Kubernetes CronJob 5 Vixie syntax with @ shortcuts
Quartz (Java) 6 or 7 Adds seconds at the front and optional year
systemd timers OnCalendar format Not cron, but solves the same problem with clearer syntax

If you write a schedule that has to run on more than one platform, stick to the conservative 5-field subset that every system understands. Reach for L, W, or # only when you know the destination supports them.

Common pitfalls

Alternatives to cron

For some workloads cron's coarse minute resolution and lack of bookkeeping start to hurt. The most common upgrades:

Tool Strength When to choose it
systemd timers Clear OnCalendar syntax, persistent across reboots, integrates with units You already run systemd and want richer logging
Anacron Catches up on missed runs after sleep Laptops or machines that are not always on
Airflow / Dagster DAG dependencies, retries, observability Multi-step data pipelines
Temporal Stateful workflows, exactly-once guarantees Long-running orchestration across services
AWS EventBridge Managed, integrates with Lambda, S3, SQS Anything cloud-native on AWS
GitHub Actions Free for public repos, runs on hosted runners CI-adjacent scheduled jobs
serverless functions on cron triggers No server to maintain Lightweight tasks that fit in a Lambda

Cron remains the right answer for the vast majority of one-shot recurring jobs. The other tools shine when you need state, retries, dependencies, or coordination across machines.

Privacy and the cron generator

The cron expression generator runs entirely in your browser. The schedule you build, the preview of next run times, and the copied expression never touch our servers. There is no log of which expressions were generated, no telemetry on which presets are popular, and no way for anyone to reconstruct what schedule you were working on. Cron expressions are not personal data on their face, but the schedule of a job (a nightly database export, a weekly billing run, a hourly sync to a partner) can reveal a lot about how a business operates. Keeping that information client-side avoids accidentally leaking infrastructure patterns to a third party. For a task as routine as picking a schedule, the privacy default should match the sensitivity of what those schedules represent.

Frequently Asked Questions

What is the cron expression format?

A standard cron expression has 5 fields separated by spaces, representing minute (0-59), hour (0-23), day of month (1-31), month (1-12), and day of week (0-6, where 0 is Sunday). An asterisk (*) means "every" value in that field.

What does */5 mean in cron?

The */5 syntax means "every 5th." In the minute field, */5 means every 5 minutes (0, 5, 10, 15...). In the hour field, */5 means every 5 hours. It works in any field.

Are cron expressions the same on all platforms?

The 5-field format is standard across Linux cron, AWS EventBridge, GitHub Actions, and most scheduling systems. Some platforms add a sixth field for seconds or year. Check your platform's documentation.

How do I schedule something for the last day of every month?

Standard cron does not have a "last day" keyword. Use a workaround like running daily and checking the date in your script, or use platform-specific extensions (AWS EventBridge supports L for "last").

Why did my cron job not run at the expected time?

The most common cause is timezone confusion. Server cron usually runs in UTC, not your local time. Other causes include the server being asleep at the scheduled minute, the user crontab not being installed, or PATH/environment differences between your shell and cron's stripped-down environment.

What is the difference between 0 in the day-of-week field and 7?

Both 0 and 7 represent Sunday in classic Vixie cron, which uses 0-6 plus an alias for 7. Some implementations (notably AWS EventBridge) use 1-7 with Sunday as 7 and Monday as 1, so always check your platform's documentation before assuming.