Cron every N minutes: step values, ranges, and why */7 misbehaves
*/5 * * * * runs a job every five minutes, and almost everyone copies that pattern without quite knowing what the slash means. The step syntax is simple once you see it clearly, but it hides a sharp edge: */7 on the minute field does not actually fire every seven minutes. Understanding why keeps you from shipping a schedule that silently drifts.
What the step syntax means
A cron field like the minute runs from 0 to 59. The expression */5 does not mean "every 5 minutes" as a duration. It means "every value in the range that is divisible by 5." The * is the full range 0 to 59, and /5 selects every fifth value starting from the bottom of that range.
So */5 expands to the explicit list:
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55
That is twelve firings per hour, evenly spaced, which is exactly what you want. The job fires at those minute marks every hour, forever.
Why */7 misbehaves
Now try */7. It selects every seventh value from 0 within the 0 to 59 range:
0, 7, 14, 21, 28, 35, 42, 49, 56
That looks fine until the hour rolls over. The next firing after 56 is not 63, because minutes only go to 59. The field resets to 0 at the top of the next hour. So the real gap between 56 and the next 0 is only four minutes, not seven.
| From | To | Gap |
|---|---|---|
| :49 | :56 | 7 min |
| :56 | :00 (next hour) | 4 min |
| :00 | :07 | 7 min |
The schedule is regular within an hour but jumps at every boundary. Any step value that does not divide evenly into 60, such as 7, 8, 9, 11, 13, has this wrap-around drift. The clean divisors of 60 are 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, and 30, and those are the only step values that produce a truly even interval.
This matters when the interval carries meaning. A health check every seven minutes that actually fires at a four-minute gap twelve times a day may trip rate limits or produce misleading timing in logs. If you genuinely need a seven-minute cadence that ignores hour boundaries, cron is the wrong tool; use a job that reschedules itself or a scheduler with true interval support.
Ranges with steps
You can apply a step to a sub-range instead of the whole field. The syntax is start-end/step.
0-29/5 * * * *
This fires every five minutes but only in the first half of each hour: 0, 5, 10, 15, 20, 25. After 25 it waits until the next hour's 0. This is handy for running something frequently during a known busy window and leaving the rest of the hour quiet.
A bare */5 is really shorthand for 0-59/5. Writing the range explicitly makes the intent obvious and sidesteps confusion about where counting starts.
Common every-N mistakes
- Assuming the slash is a duration. It selects values in a range, not a rolling interval from "now." Two jobs with
*/5added at different times still both fire at :00, :05, :10, never five minutes apart from each other. - Non-divisors of 60. As above,
*/7,*/8, and friends drift at the hour boundary. Stick to divisors unless you understand the gap. - Steps on the hour field with the same trap.
0 */5 * * *has the identical problem across the 24-hour day: 5 does not divide 24, so the overnight gap differs from the daytime gaps. - Forgetting the other fields.
*/5alone in the minute slot still needs the remaining four fields.*/5 * * * *is every five minutes every day; adding anything to the hour or day fields narrows it.
To build and preview a step expression safely, including seeing the next several run times before you trust it, use the Cron Generator and watch whether the spacing stays even across the hour boundary.