/

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

0 0 */2 * *

Common approximation: odd days of the month

Try it

0 0 1-31/2 * *

Same thing, written more explicitly

Try it

0 0 * * *

True daily, if 'every other day' turns out to be over-optimizing

Try it

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.