Cron: Every Other Day
"Every other day" seems like it should be simple, but standard cron can't truly express a rolling 2-day interval — 0 0 */2 * * is the common approximation, using the day-of-month step (1, 3, 5, 7...) instead of a genuine alternating pattern. It works most of the time, but resets at the start of every month, occasionally producing two runs closer together than 2 days apart.
0 0 */2 * *
Examples & Variations
Common Mistakes
- Assuming */2 in the day-of-month field gives a true rolling every-other-day pattern — it actually locks onto odd calendar days (1, 3, 5...), which resets at each month boundary rather than counting relative to the last run.
- Not noticing the month-boundary seam: if a month has 31 days, day 31 matches (odd), and the 1st of the next month also matches (odd) — producing two runs on consecutive days instead of the expected 2-day gap.
- Reaching for a step value here out of habit from minute/hour fields, where step syntax behaves more predictably because those fields don't reset on an irregular calendar boundary.
Best Practices
- If the occasional consecutive-day seam at month boundaries is acceptable, 0 0 */2 * * (or the equivalent 1-31/2 form) is the standard, widely-used approximation — most teams use it despite the edge case.
- For a truly rolling every-other-day interval with no exceptions, track the last run date in application state and have the script exit early if fewer than 2 days have passed — this moves the logic out of cron entirely.
- Before reaching for every-other-day, double check whether true daily (0 0 * * *) would actually serve the underlying goal just as well with less complexity.
Frequently Asked Questions
- Why isn't there a clean cron expression for 'every other day'?
- Cron's day-of-month field is calendar-based, not relative — it has no concept of 'days since last run.' The closest approximation (odd or even calendar days) inevitably has a seam at month boundaries where the actual gap is 1 day instead of 2.
- How often does the month-boundary issue actually occur?
- It depends on whether the month has an odd or even number of days. With the odd-day approximation, a 31-day month causes back-to-back runs on the 31st and the 1st of the next month; a 30-day month does not.
- What's the most reliable way to guarantee exactly 2 days between runs?
- Run the job daily via cron (0 0 * * *) and have the script itself check whether at least 2 days have passed since the last successful run, exiting immediately if not — this guarantees a true rolling interval regardless of calendar boundaries.