Cron expression for every 3 hours
The cron expression 0 */3 * * * runs a job every 3 hours on the hour — at 00:00, 03:00, 06:00, 09:00, 12:00, 15:00, 18:00 and 21:00. Eight firings per day, evenly spaced, with no day-boundary gap. The same syntax works in Linux crontab, Kubernetes CronJob and GitHub Actions. AWS EventBridge needs the 6-field form: cron(0 */3 * * ? *).
Quick reference
| Platform | Expression |
|---|---|
| Unix / Linux crontab | 0 */3 * * * |
| Kubernetes CronJob | 0 */3 * * * |
| GitHub Actions | 0 */3 * * * |
| AWS EventBridge Rules / Scheduler | cron(0 */3 * * ? *) |
| Quartz (Java) | 0 0 */3 ? * * |
Why */3 is “well-behaved” but */5 is not
Cron’s step syntax (*/N) works on every field but has a subtle gotcha: the step counts from the field’s minimum value, and the count restarts each time the next-larger field rolls over. For the hour field, that minimum is 0 and the rollover is the day boundary.
When N is a divisor of 24 — that is, 1, 2, 3, 4, 6, 8 or 12 — the firings stay evenly spaced across midnight. */3 fires at 00, 03, 06, 09, 12, 15, 18, 21 and then rolls into 00 of the next day. The gap from 21:00 to 00:00 is exactly 3 hours, same as every other gap.
When N is NOT a divisor of 24 — like 5, 7, 9, 10, 11 — the day-boundary gap is different from the in-day gap. */5 in the hour field fires at 00, 05, 10, 15, 20, then resets — a 4-hour gap from 20:00 to 00:00. If you genuinely need “every N hours” with even spacing for non-divisor N, cron alone can’t do it; use a long-running scheduler that tracks elapsed time, or an AWS rate(N hours) expression that fires N hours after the previous run regardless of clock time.
So */3 is one of the “safe” step values: pick it whenever you want exactly 8 evenly-spaced runs per day.
Variations
| Schedule | Expression |
|---|---|
| Every 3 hours, on the hour | 0 */3 * * * |
| Every 3 hours, weekdays only | 0 */3 * * 1-5 |
| Every 3 hours during business hours (9, 12, 15) | 0 9-17/3 * * * |
| Every 3 hours offset by 1 hour (1, 4, 7, 10, 13, 16, 19, 22) | 0 1-23/3 * * * |
| Every 3 hours at 30 past the hour (00:30, 03:30, …) | 30 */3 * * * |
| Every 3 hours, hourly between :00 and :02 (3 firings per slot) | 0-2 */3 * * * |
| Every 3 hours in AWS | cron(0 */3 * * ? *) |
How do I use it on each platform?
Linux crontab:
0 */3 * * * /usr/local/bin/warm-cache
Kubernetes CronJob:
apiVersion: batch/v1
kind: CronJob
metadata:
name: warm-cache
spec:
schedule: "0 */3 * * *"
startingDeadlineSeconds: 600 # tolerate 10 min of scheduler lag
jobTemplate:
spec:
template:
spec:
containers:
- name: warm
image: my-org/cache-warmer:1.0
restartPolicy: OnFailure
A 3-hour interval is short enough that occasional scheduler skips matter. startingDeadlineSeconds: 600 lets the controller catch up if the kube-controller-manager was paused or the API server was unreachable for under 10 minutes — without it, a missed slot is silently skipped until the next 3-hour mark, leaving a 6-hour gap.
AWS EventBridge (CLI):
aws events put-rule \
--name warm-cache \
--schedule-expression "cron(0 */3 * * ? *)"
GitHub Actions:
on:
schedule:
- cron: '0 */3 * * *'
GitHub Actions cron is best-effort — high-load periods can delay a scheduled run by 5–15 minutes. For an “every 3 hours” workflow that needs tight alignment, treat the actual fire time as approximate and don’t chain other jobs to its exact start.
Common mistakes
Confusing */3 in the hour field with */3 in the minute field. */3 * * * * (step in MINUTE field) means every 3 minutes — 480 runs per day, two orders of magnitude more than intended. Always pin the minute to a single value (0) when stepping the hour field.
Expecting 9-17/3 to mean “every 3 hours that falls between 9 and 17”. It actually means “starting from 9, step by 3, until 17”: 9, 12, 15 — three firings, not five. The step is anchored to the low end of the range, not interpreted as a filter on top of */3.
Forgetting AWS’s 6-field requirement. cron(0 */3 * * *) is rejected by EventBridge — the 5-field form has no AWS variant. AWS needs cron(0 */3 * * ? *) with ? for day-of-week and a trailing * for year.
Daylight Saving transitions. On the spring-forward day, the 03:00 firing is skipped (the clock jumps from 02:59 to 04:00); on fall-back, the 02:00 firing happens twice. For schedules that must run exactly 8 times per day every day of the year, set the timezone to UTC in the CronJob timeZone field (Kubernetes 1.25+) or in EventBridge’s ScheduleExpressionTimezone.
For other intervals see common cron schedules, or build a custom expression with the Cron Expression Builder.
Frequently asked questions
- Does `0 */3 * * *` produce evenly-spaced firings across midnight?
- Yes — and this is the key reason to pick `*/3` over `*/5` or `*/7`. Because 3 is a divisor of 24, the schedule fires at 00:00, 03:00, 06:00, 09:00, 12:00, 15:00, 18:00, 21:00 — eight firings, exactly 3 hours apart, with no awkward gap when the day rolls over. `*/5` in the hour field, by contrast, fires at 00, 05, 10, 15, 20 and then resets to 00 — a 4-hour gap at the day boundary, not 5.
- How is "every 3 hours" different from "every 4 hours" in practice?
- Eight firings per day vs six. The 3-hour cadence sits in the sweet spot for cache warming, light health checks and metric rollups — frequent enough that stale data is a non-issue, infrequent enough that per-run cost stays bounded. Every 4 hours starts to feel sparse for monitoring; every 2 hours starts to feel wasteful for batch work. Pick 3 when the alternatives feel either too tight or too loose.
- Can I run every 3 hours during business hours only?
- Yes — `0 9-17/3 * * *` fires at 09:00, 12:00 and 15:00 (three firings between 9 AM and 5 PM). Add weekday filtering with `0 9-17/3 * * 1-5`. Note that the step starts from the *low end of the range*, not from the field minimum — `9-17/3` means "9, 9+3, 9+6" not "every 3 hours that happens to fall in 9-17".
Need a different schedule?
Build cron expressions for Unix, Kubernetes, AWS EventBridge and Quartz — with a human-readable description and the next 5 run times.
Open the Cron Expression Builder →Related
What is a cron job?
A cron job is a scheduled command that runs at fixed times on Unix-like systems. The schedule is a one-line cron expression of five space-separated fields.
Cron expression for every 5 minutes
The cron expression `*/5 * * * *` runs every 5 minutes in Linux crontab, Kubernetes, GitHub Actions; `cron(*/5 * * * ? *)` in AWS EventBridge.
Cron expression for every 15 minutes
The cron expression `*/15 * * * *` runs every 15 minutes — at :00, :15, :30, :45 — across Linux, Kubernetes, GitHub Actions and AWS.
Cron expression for every 30 minutes
The cron expression `*/30 * * * *` runs every 30 minutes — at :00 and :30 of every hour. Same in Linux, Kubernetes and GitHub Actions; `cron(*/30 * * * ? *)` in AWS.
Cron expression for every hour
The cron expression `0 * * * *` runs once per hour on the hour. Same syntax in Linux, Kubernetes and GitHub Actions; `cron(0 * * * ? *)` in AWS EventBridge.
Cron expression for daily at midnight
The cron expression `0 0 * * *` runs once daily at midnight. Watch the timezone — Kubernetes < 1.25 and AWS EventBridge default to UTC, not local time.