Cron expressions look small, but a single field, time-zone assumption, or platform difference can make an important backup, cleanup task, report, or deployment run at the wrong time. This guide explains cron expression syntax, provides reusable CI/CD and Kubernetes examples, and gives you a practical checklist for validating schedules before they reach production.
Overview
A cron expression describes when a recurring task should run. In the traditional five-field format, the fields are read from left to right as:
| Field | Allowed values | Meaning |
|---|---|---|
| Minute | 0–59 | The minute within the hour |
| Hour | 0–23 | The hour of the day |
| Day of month | 1–31 | The calendar day |
| Month | 1–12 or names supported by the scheduler | The month of the year |
| Day of week | 0–7 or names supported by the scheduler | The weekday; 0 and 7 commonly represent Sunday |
Common operators include an asterisk for every allowed value, a comma for a list, a hyphen for a range, and a slash for an interval. For example, */15 * * * * means every 15 minutes in schedulers that use the standard five-field format. 0 2 * * 1-5 means at 02:00 on weekdays.
Cron syntax is not completely universal. Some systems add a seconds field, use a question mark, support special descriptors such as @daily, or interpret time zones differently. Treat the scheduler's own documentation as authoritative, and validate an expression against the exact platform that will execute it. A cron expression builder or local validator is useful for checking structure, but it cannot always detect platform-specific behavior.
Checklist by scenario
Nightly backups and maintenance
- Define the business meaning of “nightly.” It may mean local time for operators, UTC for infrastructure, or a low-traffic period for the application.
- Choose a schedule such as
0 2 * * *only after confirming the scheduler's time zone. - Make the job safe to retry. A failed run should not leave a partial backup, duplicate export, or locked resource.
- Record start time, completion time, exit status, and the data or environment affected.
- Set an alert for both failure and an expected run that never starts.
Reports and recurring exports
- Clarify the reporting window before writing the schedule. A Monday report may cover the previous calendar week, the previous seven days, or data available through Sunday in a particular time zone.
- Allow for upstream data pipelines to finish before the report begins.
- Use an explicit cutoff time and document whether the report includes late-arriving records.
- Test month-end, year-end, and daylight-saving transitions if the output is operationally important.
CI/CD scheduled jobs
- Check the CI platform's cron dialect and its documented time zone. Many hosted CI schedulers use UTC, but assumptions should never be left implicit.
- Pin the branch, workflow, or pipeline target so that a scheduled run does not unexpectedly execute unreviewed configuration from another ref.
- Keep scheduled jobs separate from deployment jobs unless the deployment has an approval, rollback, and notification path.
- Prevent overlapping runs when the job can take longer than its interval. Use the platform's concurrency controls or an application-level lock.
- Give the job a clear identity in logs, notifications, and audit records.
For broader pipeline design decisions, compare your scheduler's behavior with the patterns in CI/CD Pipeline Templates for GitHub Actions, GitLab CI, and Jenkins and the platform considerations in GitHub Actions vs GitLab CI.
Kubernetes CronJobs
- Start with the standard five-field
spec.scheduleformat unless your cluster documentation says otherwise. - Set a sensible
concurrencyPolicy. Use a policy that matches the job's safety requirements: overlapping runs may be acceptable for some idempotent tasks and dangerous for others. - Configure job history limits so completed objects do not accumulate indefinitely.
- Set an appropriate deadline and backoff behavior, then monitor failed Jobs rather than only the CronJob object.
- Use a container command that exits with a meaningful status code and emits enough logs to diagnose a failed run.
- Confirm the cluster and application time-zone assumptions, especially when the schedule is tied to a regional business process.
A Kubernetes CronJob schedule is only one part of reliability. Use the Kubernetes troubleshooting runbook when a scheduled Job creates a pod that is pending, repeatedly fails, or cannot reach its dependencies.
What to double-check
Time zones and daylight saving time
Write the intended time zone next to every production schedule. “Run at 02:00” is incomplete without stating where 02:00 applies. UTC is often easier to reason about for infrastructure, while local time may be required for business operations. Either choice can work if it is explicit and consistently documented.
Daylight-saving changes can create a skipped local time or a repeated local time. If a job runs at a sensitive hour, decide whether it should run once, twice, or be shifted on those dates. Prefer a scheduler with documented time-zone support, and test the behavior in a non-production environment when the schedule matters.
Day-of-month and day-of-week semantics
Expressions that specify both a day of month and a day of week are a frequent source of surprises. Traditional cron implementations may treat those fields with an OR relationship when both are restricted: the job can run when either condition matches. Other schedulers may differ. If the desired rule is complex, consider expressing it in application logic or using separate schedules with clear names.
Misfires, delays, and overlap
A cron expression describes an intended trigger, not a guarantee that work will finish at that instant. Hosts can be offline, runners can be busy, clusters can be unavailable, and queued CI jobs can start later. Decide whether a missed run should be skipped, started when capacity returns, or backfilled for each missed interval. Then make that behavior visible through logs and metrics.
For operationally important jobs, connect schedule monitoring to your observability stack. An alert based only on process failure will not detect a schedule that silently stopped triggering. Instrumenting job duration, last successful completion, and age of the most recent successful result provides a stronger signal; the OpenTelemetry setup guide offers related observability patterns.
Common mistakes
- Confusing intervals with calendar times:
*/2in the hour field means every two hours on many cron implementations, not two hours after the previous run. - Adding seconds to a five-field expression: An expression copied from a seconds-aware tool may be rejected or misread by a standard cron scheduler.
- Assuming all schedulers use local time: Hosted CI systems, containers, nodes, and control planes may use different defaults.
- Ignoring the command environment: Cron jobs often have a smaller environment than an interactive shell. Use absolute paths where appropriate, load required configuration deliberately, and avoid relying on an interactive working directory.
- Running non-idempotent work repeatedly: Retries and overlapping executions can create duplicate records, repeated notifications, or conflicting migrations.
- Testing only the expression: A valid schedule can still invoke the wrong branch, namespace, credentials, image, or database.
- Using a schedule for a dependency-driven task: If a job must run immediately after an upstream pipeline completes, an event or pipeline dependency may be safer than polling on a timer.
Apply the same caution to scheduled database changes: a recurring migration or maintenance task should have an explicit rollback and compatibility plan. The zero-downtime migration guide covers expand-and-contract patterns that can complement scheduled release workflows.
When to revisit
Revisit every production cron expression when the workflow, platform, or operating environment changes. That includes moving from a host cron daemon to a CI scheduler, migrating between CI providers, changing a Kubernetes cluster or time-zone configuration, replacing a container image, or altering the command's input and output contracts.
Review schedules before seasonal planning cycles and before periods affected by regional holidays, daylight-saving changes, financial close, or unusually high traffic. Confirm that the run time still matches the workload's dependency window and that the person or team on call knows how to respond to a missed or duplicated run.
Use this final validation checklist before publishing a schedule:
- Write the intended frequency, time zone, and business purpose in plain language.
- Count the fields and verify the syntax against the target scheduler, not only a generic cron expression builder.
- Test the next several expected run times, including a boundary such as midnight, month-end, or a daylight-saving transition when relevant.
- Confirm the command, credentials, branch, namespace, and input data are the intended ones.
- Decide how missed runs, retries, and overlapping executions should behave.
- Verify logs, metrics, notifications, and ownership before enabling the schedule.
- Record the expression and its plain-language interpretation beside the workflow configuration.
Keeping that checklist with the code turns a fragile one-line expression into a maintainable developer utility: understandable during review, testable during changes, and easier to troubleshoot when the surrounding CI/CD or cloud-native tooling evolves.