A cron expression that runs every hour:
0 * * * *
This fires at minute 0 of every hour — 24 times per day. The page above generates this and related schedules; this companion explains the hourly cadence in detail.
How the expression works
Field by field:
- Minute =
0— only the 0th minute of any hour fires. - Hour =
*— every hour 0–23. - Day-of-month, month, day-of-week — all
*, so every day.
Equivalent special-string form (in cron implementations that support it):
@hourly
Vixie cron, systemd timers, and most cloud schedulers accept @hourly. Some (notably some embedded cron implementations) don’t; the explicit five-field form is portable.
When hourly is the right cadence
- Standard refresh jobs. Cache rebuilds, content refreshes, periodic syncs that don’t need higher frequency.
- Hourly aggregations. Roll up the previous hour’s data into a summary table or report.
- Low-frequency external API calls. APIs with strict rate limits where 24 calls/day fits comfortably under quota.
- Lightweight health audits. Verify integrity of internal state once an hour.
- Log rotation triggers. Cut hourly log files at minute 0.
It’s the default cadence — most jobs that don’t have a specific reason for higher frequency end up hourly.
Hour offset variants
The minute field controls when in each hour the job fires. Some examples:
0 * * * * ← :00 of every hour (canonical hourly)
5 * * * * ← :05 of every hour
30 * * * * ← :30 of every hour
0,30 * * * * ← :00 and :30 — twice an hour, see cron-every-30-minutes
15 9-17 * * 1-5 ← :15 of every hour, 9 AM–5 PM, weekdays
A common practice: stagger downstream-of-each-other jobs so they don’t fire simultaneously. If job A produces output that job B consumes, schedule A at 0 * * * * and B at 15 * * * * — gives A 15 minutes to finish before B starts.
Hourly vs 0 0-23 * * *
These look similar but mean different things:
0 * * * *— minute 0 of every hour. Hourly.0 0-23 * * *— minute 0 of every hour from 0 to 23. Same as hourly.0 0 * * *— minute 0 of hour 0. Daily at midnight, not hourly.
The third one is the common typo. If a “hourly” job suddenly only fires once a day after a config change, the hour field probably got narrowed.
Hourly during business hours
If hourly is overkill at night, restrict the hour field:
0 9-17 * * * ← :00 of 9 AM–5 PM (9 fires/day)
0 9-17 * * 1-5 ← only weekdays (45 fires/week instead of 168)
0 8-20 * * * ← longer business window
The end of the range is inclusive — 9-17 covers 9, 10, 11, …, 17 (which is 5 PM, but the job at 5 PM fires at the start of 5 PM, not at 6 PM).
Common pitfalls
- Many services all firing at
0 * * * *. The top of the hour becomes a load spike. Stagger by minute (0 * * * *,5 * * * *,10 * * * *, …) when multiple jobs run on the same infrastructure. - Long-running hourly jobs. A 65-minute job on
0 * * * *overlaps with itself. Always either run less frequently or wrap with overlap protection. - Forgetting timezones. A job set to “hourly” in local time has the same number of fires per day as in UTC, but the wall-clock alignment differs. Daylight saving doesn’t affect hourly the way it affects daily, but the moment each hourly fire happens still shifts.
- Using
* * * * *thinking it means hourly. That’s “every minute” — 1440 fires per day, not 24. The minute field controls when, not how often. - Cron daemons under heavy load. When the system is overloaded and cron’s fork-and-run takes longer than expected, “every hour” can drift to “every 65 minutes” or worse. cron is best-effort, not real-time guaranteed.
For sub-hour cadences, see cron every 30 minutes, cron every 15 minutes, cron every 5 minutes, cron every minute. For longer cadences, cron daily, cron weekly, cron monthly.