⚙️ Cron Expression Builder

Last updated: November 24, 2025

⚙️ Cron Expression Builder

Build, decode, and preview cron schedule expressions. Edit any field or type directly.

Copied!
0 – 59
0 – 23
1 – 31
1 – 12
0=Sun – 6=Sat
Plain English Description
At exactly 9:00 AM, Monday through Friday
Next 10 Scheduled Runs
    Quick Syntax Reference
    *Any / every value
    */nEvery n-th value
    a-bRange from a to b
    a,b,cSpecific values list
    a-b/nEvery n within range
    5 fieldsmin hr dom mon dow

    What Cron Expressions Actually Mean — And Why Getting Them Right Matters

    Every developer has been there: a scheduled job that was supposed to run at 9 AM every weekday silently fires at midnight, or worse, runs every single minute because someone wrote * * * * * when they meant 0 0 * * *. Cron expression errors are insidious. They don't throw exceptions. They don't fail loudly. They either run too often, not often enough, or never at all — and you only notice when the nightly report is missing or the server has been hammering the database every 60 seconds for three days.

    The cron scheduling system has been around since Version 7 Unix in 1979, and its five-field notation has barely changed. That longevity is a testament to its elegance, but also its terseness. A string like 0 9 * * 1-5 is perfectly readable once you know what you're looking at, but opaque to anyone who hasn't memorized the field order.

    The Five Fields, Explained Without Hand-Waving

    A standard cron expression has exactly five space-separated fields, read left to right: minute (0–59), hour (0–23), day of month (1–31), month (1–12), and day of week (0–6, where 0 is Sunday). The mental model that makes these stick is: "When during the hour? Which hour? Which day of the month? Which month? Which day of the week?"

    Each field accepts four kinds of values. A bare number (9) means exactly that value. An asterisk (*) means every valid value. A hyphenated range (1-5) means every value from the first to the second, inclusive. A step expression (*/15 or 0-30/10) means every Nth value — either across the full range or within a specified sub-range. And comma-separated values (1,15,28) let you specify an explicit list.

    These can be combined: 0,30 8-18 * * 1-5 means "at minute 0 and minute 30, during hours 8 through 18, every day of the month, every month, on weekdays only" — in plain English, twice per hour during business hours on working days.

    The Day-of-Month vs Day-of-Week Trap

    The interaction between the fourth and fifth fields trips up experienced engineers. When both day-of-month and day-of-week are set to something other than *, most cron implementations (including Vixie cron, which is what Linux uses) treat them as a union rather than an intersection. 0 9 1 * 1 does NOT mean "9am on the 1st of the month only if it's a Monday" — it means "9am on the 1st of the month OR any Monday." If you wanted the intersection, you need to handle that in your script logic, not in the cron expression itself.

    This is one of the most quietly broken assumptions in scheduled task management. A team sets up a report to run on the 15th on weekdays, then notices it's running on random Mondays and Wednesdays throughout the month. The fix is straightforward once you know the rule: use only one day-dimension at a time in the cron expression and use the other in your script's guard clause if needed.

    Step Values and Their Practical Uses

    The slash syntax is what makes cron expressions genuinely powerful for periodic scheduling. */5 in the minute field means "every 5 minutes," which is far cleaner than listing 0,5,10,15,20,25,30,35,40,45,50,55. But the more nuanced form, start-end/step, is underused. 0-30/10 * * * * runs at minutes 0, 10, 20, and 30 — then stops. It does not run at 40 or 50. This is useful for jobs that need to run only in the first half of the hour, or for rate-limiting a polling task during a specific window.

    For hour-level granularity: 0 */4 * * * runs at 00:00, 04:00, 08:00, 12:00, 16:00, and 20:00 every day. Note that it always starts from hour 0, not from when the daemon started. This "anchored to epoch" behavior is important: */4 will always give you those specific hours, not "every 4 hours from some arbitrary start time."

    Common Real-World Patterns

    Several cron patterns appear so frequently they're worth memorising. 0 0 * * * is the classic daily midnight run — used for log rotation, nightly backups, and daily report generation. 0 0 1 * * starts monthly jobs on the first of the month, perfect for billing cycles and monthly summaries. 0 0 * * 0 runs weekly on Sunday midnight, common for heavier maintenance tasks. */5 * * * * is the go-to for lightweight polling — checking a queue, syncing a feed, or pinging an external service. And 0 9-17 * * 1-5 runs hourly during business hours on weekdays, useful for jobs that only make sense when staff are present.

    For deployment pipelines and CI systems, 0 2 * * 1-5 — 2 AM on weekdays — is popular because it gives engineers something to wake up to if it fails, rather than a weekend incident nobody notices until Monday.

    Environment Differences to Watch For

    Not all cron implementations are identical. Quartz Scheduler (common in Java applications) uses a six-field format with seconds as the first field and optionally a seventh field for year. AWS EventBridge also uses a six-field format but with years. If you're building expressions for Kubernetes CronJobs or GitHub Actions scheduled workflows, you're back to standard five-field Vixie cron syntax. Always verify which implementation your platform uses — the visual preview of upcoming runs is your best sanity check, because the same string can mean different things in different systems.

    Timezone is another silent failure mode. Most cron daemons run in the system's local timezone, which means a server configured in UTC will fire your 0 9 * * * at 9 AM UTC, not 9 AM in your users' timezone. Cloud schedulers like AWS EventBridge let you specify a timezone explicitly. For self-hosted cron, prepend CRON_TZ=America/New_York (on systemd timers or GNU cron) or handle the offset yourself.

    Testing Before You Deploy

    The single most important habit when working with cron expressions is previewing the next several run times before deploying. An expression that looks reasonable can still be subtly wrong — 0 0 30 2 * will never fire because February never has a 30th day. 0 0 31 * * silently skips April, June, September, and November. These are exactly the kinds of mistakes that slip past code review because the expression parses without error.

    Generating the next 10 or 20 scheduled times takes the guesswork out entirely. If the list shows runs happening at unexpected intervals, or shows no runs at all for months, you catch the problem before it becomes a production incident. Visual builders that show both the plain-English description and the concrete upcoming timestamps serve as a double check: the description tells you what you intended, and the timestamps tell you what the system will actually do.

    Cron expressions are deceptively small. Five fields, a handful of operators, and you can describe almost any repeating schedule imaginable. The craft is in knowing the edge cases well enough that your 3-line string does exactly what you expect — no more, no less — every single time the scheduler fires.

    FAQ

    What does each field in a cron expression represent?
    A standard cron expression has five space-separated fields in this exact order: minute (0–59), hour (0–23), day of month (1–31), month (1–12), and day of week (0–6, where 0 is Sunday and 6 is Saturday). For example, '30 14 * * 5' means 'at 2:30 PM every Friday.'
    What is the difference between '*' and '*/1' in a cron field?
    They are functionally identical — both mean 'every value in the valid range.' The asterisk alone ('*') is the conventional shorthand. '*/1' is valid but redundant; you'd typically use '*/2', '*/5', '*/15' and so on to mean 'every 2nd', 'every 5th', or 'every 15th' value.
    Why does my cron job run on unexpected days when I set both day-of-month and day-of-week?
    When both the day-of-month and day-of-week fields are set to a non-wildcard value, standard cron (Vixie cron) uses a union, not an intersection. '0 9 1 * 1' means '9 AM on the 1st of the month OR 9 AM on any Monday' — not '9 AM on the 1st only when it falls on a Monday.' To enforce an intersection, keep one of the day fields as '*' and check the other condition inside your script.
    How do I schedule a job to run every 30 minutes only during business hours?
    Use '0,30 9-17 * * 1-5'. This fires at minute 0 and minute 30 (twice per hour), during hours 9 through 17, every day of the month, every month, and only on Monday through Friday. Adjust the hour range and day-of-week range to match your timezone and business calendar.
    Will a cron expression like '0 0 31 * *' run in February, April, or November?
    No. Months with fewer than 31 days will silently skip that month's run — no error is raised. February never has a 31st day, and April, June, September, and November have only 30. If you want a job to run on the last day of every month, some cron implementations support the 'L' shorthand in the day-of-month field, or you can schedule for the 28th and check the date inside the script.
    Does the cron builder work for Kubernetes CronJobs, GitHub Actions, and AWS EventBridge?
    Kubernetes CronJobs and GitHub Actions scheduled workflows both use the standard five-field cron format, so expressions built here work directly. AWS EventBridge uses a six-field format that adds a year field at the end and uses '?' instead of '*' for unused day fields — you will need to adapt the expression slightly for EventBridge. Always preview the upcoming run times in the target platform's scheduler before deploying.