Reading 0 * * * *
Minute 0, every hour, every day, every month, every weekday: the job fires exactly at the
top of each hour, 24 times a day. This is the single most common cron mistake territory — beginners
write * * * * * hoping for “hourly” and get a run every minute
instead, because the leftmost field is the minute, not the hour. If you only remember one rule, make it
this one: to run hourly, fix the minute.
On the hour, or half past?
Nothing forces minute 0. 30 * * * * runs at half past every hour, and any minute works:
17 * * * * fires at :17. Just like midnight, the top of the hour is when everyone
else’s jobs fire too, so a random-looking fixed minute both spreads load and makes your job easy to
find in logs. What you cannot do in a single 5-field expression is “every 90 minutes”
— 90 does not divide the day evenly through the minute field alone. The usual workaround is two crontab
lines (0 0-21/3 * * * and 30 1-22/3 * * *) that interleave.
Hourly variants
| Expression | Meaning |
|---|---|
0 * * * * | Every hour, on the hour |
30 * * * * | Every hour at half past |
0 */2 * * * | Every 2 hours (00:00, 02:00 …) |
0 */6 * * * | Every 6 hours (00:00, 06:00, 12:00, 18:00) |
0 9-17 * * 1-5 | Hourly, 09:00–17:00, weekdays |
0 8,12,18 * * * | Three fixed times a day |
Note how hour steps anchor at 0: 0 */5 * * * yields 00:00, 05:00, 10:00, 15:00, 20:00 and
then wraps to 00:00 — the gaps are 5,5,5,5,4 hours. If you need perfectly even spacing, pick a
divisor of 24 (2, 3, 4, 6, 8, 12).
What hourly jobs are good for
- Syncing data that changes throughout the day but does not need real-time freshness.
- Refreshing caches, sitemaps or search indexes.
- Retrying failed webhooks or stuck queue items.
- Rolling up per-minute metrics into hourly aggregates.
One DST caveat for hour-level schedules: on spring-forward night, a skipped local hour means an hour-range job can lose a run, and on fall-back night an ambiguous hour can add one, depending on the cron implementation. Vixie cron compensates for shifts up to 3 hours; simpler libraries may not. Hourly jobs pinned to minute 0 across all hours are unaffected in practice — only ranges touching the transition window need thought. The preview above follows your OS clock, DST included.
FAQ
What is the cron expression for every hour?
0 * * * * — minute 0 of every hour, i.e. on the hour: 00:00, 01:00, 02:00 and so on. The shorthand @hourly is equivalent where macros are supported.
Why is * * * * * not "every hour"?
Because the first field is the minute. * * * * * matches every minute — 60 runs per hour. To run hourly you must pin the minute to a single value, most commonly 0.
How do I run every 2, 3 or 6 hours?
Step the hour field: 0 */2 * * * (every 2nd hour: 00:00, 02:00 …), 0 */3 * * * , 0 */6 * * *. Steps count from hour 0, so 0 */5 * * * gives 00:00, 05:00, 10:00, 15:00, 20:00 — and then 00:00 again, so the last gap of the day is only 4 hours.
How do I run hourly but only during working hours?
Use an hour range: 0 9-17 * * 1-5 runs at 09:00 through 17:00 inclusive, Monday to Friday — nine runs per weekday.
Related: cron every 5 minutes · cron every day at midnight · full cron expression generator