A reliable cron expression starts with understanding the scheduler that will run it. This guide explains cron syntax field by field, provides practical schedule examples, compares common cron variants, and gives you a repeatable checklist for checking time zones, daylight-saving changes, permissions, logging, and missed jobs.
Overview
A cron expression is a compact schedule definition. In the traditional Unix format, it usually contains five fields that describe when a command should run:
┌──────── minute (0–59)
│ ┌────── hour (0–23)
│ │ ┌──── day of month (1–31)
│ │ │ ┌── month (1–12 or names)
│ │ │ │ ┌ day of week (0–7 or names)
│ │ │ │ │
* * * * * commandThe asterisk means “every allowed value”. A comma lists separate values, a hyphen defines a range, and a slash defines a step. For example:
*means every value.1,15means values 1 and 15.1-5means values from 1 through 5.*/10means every 10th value within the field.
These operators can be combined. 0,30 in the minute field runs at the start and middle of each hour, while 9-17/2 in an hour field selects every two hours from 09:00 through 17:00.
Always confirm the format before copying an expression into production. Standard user crontabs commonly use five schedule fields followed by a command. Some systems add a seconds field, and system-level cron files may include an extra username field between the schedule and command. Cloud schedulers, CI platforms, container tools, and application frameworks may use their own syntax or timezone rules. A cron schedule generator can help draft an expression, but the generated result still needs to be checked against the target scheduler.
Common cron syntax examples
| Expression | Meaning in a standard five-field format |
|---|---|
0 2 * * * | Every day at 02:00 |
*/15 * * * * | Every 15 minutes |
0 9 * * 1-5 | At 09:00, Monday through Friday |
30 8 1 * * | At 08:30 on the first day of each month |
0 0 1 1 * | At midnight on 1 January |
0 18 * * 0 | At 18:00 on Sunday where 0 represents Sunday |
Day-of-week numbering is not identical everywhere. Many Unix implementations accept both 0 and 7 for Sunday, but relying on that assumption can make migrations harder. Named values such as MON or JAN may also be supported, although spelling, case, and accepted abbreviations depend on the implementation.
Checklist by scenario
Writing a new daily or weekly job
- Write the requirement in plain language first: “run at 02:15 every day” or “run at 09:00 on weekdays”.
- Translate the requirement one field at a time rather than guessing from a five-asterisk pattern.
- Confirm whether the scheduler uses the host’s local time, UTC, or a configured timezone.
- Use an absolute command path where practical, because cron may provide a smaller environment than your interactive shell.
- Run the command manually as the same user that will own the scheduled job.
- Redirect output to a deliberate log destination or configure the platform’s logging mechanism.
For example, a weekday job at 09:00 in a conventional five-field format is 0 9 * * 1-5. That expression describes the recurring time; it does not guarantee that the command has the permissions, environment variables, network access, or working directory it needs.
Scheduling monthly, quarterly, or annual work
Monthly schedules are easy to misunderstand because months have different lengths. 0 3 31 * * means 03:00 on the 31st day of months where that date exists; it does not mean “the last day of every month”. If the business requirement is the last calendar day, use a scheduler feature designed for that rule or schedule a daily check that tests the date explicitly.
Similarly, a quarterly task can be expressed as selected months, such as 0 4 1 1,4,7,10 * for 04:00 on the first day of January, April, July, and October. Document the intended months in a comment or runbook so that the expression remains understandable during an audit.
Moving a job between platforms
- Identify the source format: standard Unix cron, a system cron file, a scheduler with seconds, or a hosted platform.
- Check whether the destination adds a seconds field or a username field.
- Check support for ranges, steps, named months, named weekdays, and special strings such as shortcuts.
- Confirm how the destination interprets day-of-month together with day-of-week.
- Test the expression in a staging or non-destructive context before enabling the real command.
- Record the scheduler, expression, timezone, owner, command path, and expected output.
Do not assume that an expression copied from a Linux crontab will work unchanged in a container orchestration platform, CI service, or cloud scheduler. Similar-looking syntax can conceal different field counts and calendar behavior.
Debugging a missed or repeated job
- Verify that the crontab was saved in the location and account you intended.
- Check the scheduler service and its logs.
- Confirm the expression with an independent parser or controlled test.
- Compare the scheduler’s timezone with the time shown in the log timestamps.
- Run the command with a minimal environment and capture standard output and errors.
- Check file permissions, executable bits, secrets, network access, and the working directory.
- Look for overlapping runs, lock files, timeouts, and duplicate scheduler registrations.
If a job appears to run twice, first check whether two schedules, two hosts, or two deployment versions are active. A valid expression can still produce duplicate work when the surrounding deployment has been duplicated.
What to double-check
Time zones and daylight-saving changes
A cron job time is meaningful only alongside its timezone. A server may use UTC while an operator reads local time, or a scheduler may allow a timezone setting that overrides the host. Write the timezone next to every important schedule, especially for billing, reports, backups, and customer notifications.
Daylight-saving transitions create two unusual cases. A local time may not occur on the spring transition, while a local time may occur twice on the autumn transition. Scheduler behavior varies: a job might be skipped, run once, or be evaluated according to a platform-specific rule. If a task must run exactly once per elapsed interval, consider scheduling in UTC or designing the command to be idempotent and to record its execution date.
Day-of-month and day-of-week logic
When both the day-of-month and day-of-week fields contain restricted values, implementations may treat them as alternatives rather than requiring both conditions. This is a common source of surprises. For a rule such as “at 10:00 on the first weekday of the month”, do not rely on an ambiguous expression. Use a tested scheduler feature or a command that checks the date explicitly.
Execution context
Cron does not necessarily run with the same shell, PATH, home directory, locale, or environment variables as your terminal. Use explicit paths, set required variables safely, choose the intended shell, and avoid placing credentials directly in a crontab. A small wrapper script can make setup, logging, retries, and exit codes easier to test.
For related workflow reliability practices, see the LLM evaluation guide for a broader approach to testing automated processes, or review the developer tools guide for utilities that can help inspect configuration and output.
Common mistakes
- Using six fields in a five-field scheduler: A seconds field may shift every value and produce an entirely different schedule.
- Confusing “every 15 minutes” with “at minute 15”:
*/15 * * * *runs at 00, 15, 30, and 45 minutes;15 * * * *runs once per hour at minute 15. - Assuming a step means a calendar interval:
*/2in a month field selects even-positioned months; it does not calculate two months after the previous execution. - Scheduling the 31st as the last day: Shorter months will not match that date.
- Ignoring command exit status: A scheduler starting a process does not prove that the process completed successfully.
- Forgetting overlap: If a task takes longer than its interval, another invocation may start before the first has finished unless locking or concurrency control is provided.
- Testing only the expression: A parser can confirm the calendar match but cannot confirm permissions, dependencies, network access, or application correctness.
- Leaving undocumented schedules behind: Every production job should have an owner, purpose, timezone, expected frequency, and removal or review process.
When to revisit
Revisit a cron expression before seasonal planning cycles, timezone changes, infrastructure migrations, operating-system upgrades, and major workflow changes. Pay particular attention before periods that cross daylight-saving transitions or contain month-end and year-end processing.
Review the schedule whenever the command moves to a new host, container, account, region, CI system, or cloud service. The expression may remain identical while the timezone, environment, permissions, logging, or retry behavior changes. Also revisit jobs after an incident, a missed deadline, an unexpected duplicate, or a change to the application’s runtime.
Use this final pre-flight checklist:
- State the intended schedule in plain language.
- Count the fields and verify the destination’s cron dialect.
- Test the expression with representative dates, including month boundaries and daylight-saving transitions where relevant.
- Write down the timezone and confirm it in the runtime environment.
- Run the command as the scheduled user with a minimal environment.
- Confirm logs, alerts, exit-code handling, locking, and retry behavior.
- Record the owner and review date in the job’s documentation.
A cron expression is only one part of dependable automation. Treat it as configuration that needs verification, observability, and periodic review, not as a self-explanatory line of punctuation.