Cron jobs look simple until a task runs twice, misses a daylight-saving transition, or behaves differently after a hosting migration. This guide explains standard cron syntax, gives practical schedule examples, compares common deployment environments, and provides a validation and monitoring workflow you can use before and after putting recurring jobs into production.
Overview
A cron expression describes when a recurring task should run. In the traditional Unix format, it contains five time fields followed by the command:
minute hour day-of-month month day-of-week command
For example:
30 2 * * * /path/to/backup.sh
This means “run at 02:30 every day,” assuming the scheduler uses the expected timezone and a five-field cron syntax. The expression is only one part of a reliable scheduled job. The deployment environment also determines where the command runs, which user owns it, what environment variables are available, how failures are reported, and whether overlapping executions are allowed.
Before using a cron expression builder or writing an entry manually, identify the scheduler. A Linux system crontab, a container platform, a CI runner, and a managed hosting service may accept similar-looking expressions while applying different limits or timezone rules. Treat the platform’s scheduler documentation as the final authority, then validate the expression with a tool or a local test harness.
What to track
Cron syntax and field meaning
The standard five-field form is:
| Field | Allowed values | Example | Meaning |
|---|---|---|---|
| Minute | 0–59 | 15 | At 15 minutes past the hour |
| Hour | 0–23 | 6 | At 06:00 in the scheduler’s timezone |
| Day of month | 1–31 | 1 | On the first calendar day |
| Month | 1–12 | 1,7 | In January and July |
| Day of week | Usually 0–7 | 1-5 | Monday through Friday in many implementations |
An asterisk means “every permitted value.” A comma lists separate values, a hyphen defines a range, and a slash defines a step. These examples illustrate the building blocks:
*/10 * * * *runs every ten minutes.0 9 * * 1-5runs at 09:00 on weekdays.0 0 1 * *runs at midnight on the first day of each month.0 */4 * * *runs at the beginning of every fourth hour.
Be cautious when both day-of-month and day-of-week contain values. Their matching behavior differs among cron implementations; some run when either field matches rather than requiring both. If a schedule depends on an exact calendar rule, test it against the target scheduler instead of assuming that a familiar expression has identical semantics everywhere.
Timezone and daylight-saving behavior
“02:00” is incomplete unless you know which timezone it refers to. Record the intended timezone beside every production schedule, especially when the application, database, and hosting platform use different defaults. A job configured for UTC is easier to reason about across regions, while a business process tied to local midnight may require an explicit regional timezone.
Daylight-saving transitions can create a missing local time in spring and a repeated local time in autumn. Depending on the scheduler, a job may be skipped, run once, or run more than once. For billing, notifications, imports, and other non-idempotent work, design the task so repeated execution is safe. A durable approach is to store a job key or execution window and reject work that has already been completed.
Platform behavior
| Environment | What to verify | Operational concern |
|---|---|---|
| Server or VM crontab | Five-field syntax, user, PATH, shell, and timezone | Commands may work interactively but fail with cron’s smaller environment |
| Container deployment | Whether the scheduler persists across restarts and which process owns it | Multiple replicas can trigger duplicate runs |
| CI/CD scheduler | Expression dialect, repository permissions, and runner timezone | Schedules may be delayed when runners or workflows are unavailable |
| Managed hosting task runner | Supported frequency, timezone configuration, timeout, and overlap rules | Platform limits may differ from local cron |
Keep the schedule definition and the task’s operational assumptions together in version control. If you are also investigating failed requests or deployment behavior, the Chrome DevTools Network Tab guide and the HTTP status code troubleshooting guide can help with adjacent debugging work.
Cadence and checkpoints
Use a three-stage validation workflow rather than placing an untested expression directly into production.
- Describe the schedule in plain language. Write “every weekday at 09:15 UTC” before writing
15 9 * * 1-5. This catches many mistakes involving midnight, weekdays, and step values. - Generate and validate the expression. An online cron expression builder or validator is useful for checking the next several run times. Compare those results with the platform’s own documentation and test interface.
- Run the command outside the scheduler. Confirm paths, permissions, credentials, database access, and required environment variables. Use absolute paths where practical, and send output to a controlled log destination.
- Deploy with a harmless checkpoint. Initially have the job write a timestamp, emit a structured log, or update a test record. Avoid making the first production test destructive or difficult to reverse.
- Observe several expected runs. Check scheduled time, start time, completion time, exit status, and output. A job that starts on time but consistently exceeds its interval needs a different control strategy.
For frequent schedules, decide whether overlapping runs are acceptable. If not, use a lock, database lease, queue-based worker, or platform-provided concurrency control. A lock should also have an expiry or recovery plan so a crashed process does not block every future run.
Review scheduled jobs monthly or quarterly, depending on their importance. At each checkpoint, record the configured expression, timezone, recent successful and failed runs, average duration, maximum duration, and whether any manual reruns occurred. This turns a hidden automation task into an auditable deployment component.
How to interpret changes
A schedule problem is not always a syntax problem. Compare the planned time with the actual start time and then separate the possible causes:
- Every run is shifted: check timezone, daylight-saving configuration, and whether the platform interprets the expression in UTC.
- Runs happen twice: look for duplicate deployments, multiple application replicas, overlapping invocations, or day-of-month/day-of-week matching behavior.
- A run is missing: inspect invalid dates, suspended deployments, unavailable runners, timeouts, and local times that do not exist during a clock change.
- The job starts but fails: compare the scheduler’s environment with your shell, including PATH, working directory, secrets, network permissions, and runtime version.
- The job is increasingly late: measure queue delay and execution time separately. A slow task may need batching, pagination, a queue, or a longer interval.
Use idempotency and observability together. A unique execution identifier, structured logs, an explicit outcome, and an alert on repeated failure make it possible to distinguish “ran twice safely” from “performed the same side effect twice.” Keep secrets out of command-line arguments and logs, and make manual replay an intentional operation with clear boundaries.
When to revisit
Revisit a cron expression whenever the application moves hosts, changes timezone assumptions, adds replicas, changes its runtime, or modifies the command’s dependencies. Also review it after a missed or duplicate run, a daylight-saving transition, a database migration, or a change to retention and notification rules.
For a recurring maintenance review, use this checklist:
- State the intended schedule in plain language and confirm the configured timezone.
- Validate the expression and inspect its next several run times.
- Confirm the deployment platform still supports the expression, frequency, timeout, and required permissions.
- Check that only the intended scheduler instance can invoke the job.
- Verify idempotency, locking, retry behavior, and failure alerts.
- Compare recent start times and durations with the expected window.
- Document the owner, last review date, rollback method, and safe manual replay procedure.
When a platform changes its scheduling system, do not rely on a visual match between the old and new expressions. Recreate the schedule, compare generated run times, test it in a non-destructive environment, and monitor the first production executions. A small quarterly review is usually enough to catch timezone drift, duplicate schedulers, and stale deployment assumptions before they become operational incidents.