Skip to main content
Tools Harbor

Cron expression for every 2 hours

The cron expression 0 */2 * * * runs a job every 2 hours on the hour — at 00:00, 02:00, 04:00, 06:00, 08:00, 10:00, 12:00, 14:00, 16:00, 18:00, 20:00 and 22:00 (12 firings per day). The */2 is step syntax: “every 2 units starting from 0”. The step works in any cron field; */2 in the day-of-month field would mean “every other day”, */2 in the minute field means “every other minute”.

Quick reference

PlatformExpression
Unix / Linux crontab0 */2 * * *
Kubernetes CronJob0 */2 * * *
GitHub Actions0 */2 * * *
AWS EventBridgecron(0 0/2 * * ? *)
Quartz (Java)0 0 0/2 ? * *

How does step syntax (*/N) work?

*/N reads as “every N units starting from the lowest legal value of this field”. In the minute field (range 0–59), */15 hits 0, 15, 30, 45. In the hour field (range 0–23), */2 hits 0, 2, 4, …, 22. In the day-of-month field (range 1–31), */5 hits 1, 6, 11, 16, 21, 26, 31.

The “starting from 0” matters when N is not a divisor of the field’s range. For divisors of 24 — */2, */3, */4, */6, */8, */12 — the firings are evenly spaced across the day. For non-divisors — */5, */7, */9, */11 — there’s a gap at the day boundary because the count restarts at hour 0.

For example, */5 * * * in the hour field fires at 0, 5, 10, 15, 20. Then the day rolls over and the next firing is at hour 0 of the next day — a 4-hour gap, not 5.

If you want truly evenly-spaced “every N hours” and N is not a divisor of 24, you can’t get it with cron alone. Use a long-running scheduler that tracks elapsed time, or accept the day-boundary gap.

Variations

ScheduleExpression
Every 2 hours on the hour0 */2 * * *
Every 2 hours starting at 1 AM (1, 3, 5, 7, …)0 1-23/2 * * *
Every 2 hours during business hours (9 AM – 5 PM)0 9-17/2 * * *
Every 3 hours0 */3 * * *
Every 4 hours0 */4 * * *
Every 6 hours0 */6 * * *
Every 12 hours (twice daily)0 */12 * * * (= 0 0,12 * * *)
Every 2 hours in AWScron(0 0/2 * * ? *)

How do I use it on each platform?

Linux crontab:

0 */2 * * * /usr/local/bin/health-check

Kubernetes CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: health-check
spec:
  schedule: "0 */2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: check
              image: my-org/health-check:1.0
          restartPolicy: OnFailure

AWS EventBridge:

aws events put-rule \
  --name health-check \
  --schedule-expression "cron(0 0/2 * * ? *)"

EventBridge’s rate(2 hours) is an alternative that fires every 2 hours from rule-creation time — no clock alignment. Use cron() for clock-aligned 00:00, 02:00, 04:00; use rate() if you don’t care about the wall-clock minute.

GitHub Actions:

on:
  schedule:
    - cron: '0 */2 * * *'

Common mistakes

Expecting */5 in the hour field to be evenly spaced across the day boundary. It hits 0, 5, 10, 15, 20 — then resets to 0 the next day, leaving a 4-hour gap. If you need “every 5 hours from when the job was created”, use a rate() expression or a long-running scheduler.

Putting the step in the wrong field. */2 * * * * (step in MINUTE field) means every 2 minutes — 720 runs per day. 0 */2 * * * is what you want for every 2 hours. Easy mis-paste.

Using 0 */2 * * ? * in AWS without 0/2. Both work in modern AWS, but the older docs say only 0/2 is valid. For maximum compatibility with old IaC validators and copy-paste guides, prefer 0/2.

For other schedules see common cron schedules, or build a custom expression with the Cron Expression Builder.

Frequently asked questions

How does the step syntax `*/2` work?
`*/N` means "every N units, starting from the lowest legal value". In the hour field, `*/2` fires at 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 — every other hour. In the minute field `*/15` fires at 0, 15, 30, 45. The step starts from the field minimum, not from when cron started.
Why doesn''t `0 */5 * * *` run "every 5 hours"?
It does run every 5 hours in a sense, but the result might surprise you. `*/5` in the hour field hits 0, 5, 10, 15, 20 — five firings per day, with a 4-hour gap between hour 20 and hour 0 of the next day (because the day rolls over and the step resets). For divisors of 24 (`*/2`, `*/3`, `*/4`, `*/6`, `*/8`, `*/12`), the gap is uniform. For non-divisors (`*/5`, `*/7`, `*/9`, `*/11`), the schedule is uneven across the day boundary.
What''s the difference between `*/2` and `0/2` in the hour field?
`*/2` and `0/2` are equivalent in standard Unix cron — both mean "every 2 starting from 0". AWS EventBridge prefers `0/2` syntactically; older AWS docs claim `*/2` is invalid (it isn't — it works), but `0/2` is the safer form for AWS-targeted IaC. To start at a different value, use the explicit form: `5/2` means 5, 7, 9, 11, 13, 15, 17, 19, 21, 23.

Need a different schedule?

Build cron expressions for Unix, Kubernetes and AWS — with a human-readable description and the next 5 run times.

Open the Cron Expression Builder →

Related