Every Hour Cron Expression
Runs at minute 0 of every hour — 24 times per day.
0 * * * * in POSIX cron
Expression
0 * * * *Try it in the tester →Field Breakdown
How It Works
Minute pinned to 0, hour wildcard — fires once at the top of every hour.
Example Run Times
- 12:00:00
- 13:00:00
- 14:00:00
- 15:00:00
- 16:00:00
Frequently Asked Questions
Is `0 * * * *` the same as `@hourly`?
Yes — `@hourly` is the Vixie-cron alias that expands to `0 * * * *`.
Why not `* */1 * * *`?
`* */1 * * *` is `*` in the minute field — it would fire every minute, not once per hour.
0 * * * * in Quartz / Spring
With seconds and minutes both pinned to 0 and the hour left wild, this trigger fires exactly at the top of each hour, 24 times a day. It's probably the single most common cron string in Java codebases.
Quartz / Spring expression
0 0 * * * ?Try it in the tester →At second 0, at minute 0, every hour
Unix / POSIX equivalent: 0 * * * *
Field Breakdown
Using It with Quartz / Spring
Hourly jobs cover session-table cleanup, log compaction, metrics rollups, and cache eviction sweeps. Spring 5.3 and later also accept the macro @Scheduled(cron = "@hourly"), which schedules the same top-of-hour fire times (Spring expands it to 0 0 * * * * — its parser doesn't require the ? Quartz does); older Spring versions and standalone Quartz need the literal six-field form. In clustered Quartz, hourly is where teams usually flip on JDBC job storage so only one node in the cluster claims each firing.
Quartz-Specific Notes
- →Top-of-hour is a thundering-herd hotspot — if a dozen jobs all use 0 0 * * * ?, stagger some onto offset minutes like 0 7 * * * ? to flatten the load spike.
- →The first wildcard position that matters is the hour field; the two zeros in front are seconds and minutes, a frequent off-by-one trap for people reading it as Unix cron.
- →Plain Quartz's CronExpression does not understand @hourly — macros are a Spring Framework 5.3+ convenience layered on top.
Frequently Asked Questions
Is "@hourly" valid in Quartz?
Not in Quartz itself. Spring 5.3+ accepts the @hourly macro and schedules the same top-of-hour fire times (its own parser expands it to 0 0 * * * *, without the ? Quartz requires), but a raw Quartz CronTrigger will throw a ParseException on the macro string.
How do I run hourly but at 15 past instead of on the hour?
Change the minute field: 0 15 * * * ? fires at :15 of every hour. Only the second field stays 0.
0 * * * * in AWS EventBridge
Minute pinned to 0 with the hour left as a wildcard: this rule triggers at the top of every hour, 24 times per UTC day. Hourly is the default heartbeat for an enormous share of AWS housekeeping automation.
AWS EventBridge expression
cron(0 * * * ? *)Try it in the tester →At minute 0 of every hour UTC
Unix / POSIX equivalent: 0 * * * *
Field Breakdown
Using It with AWS EventBridge
Use the string as ScheduleExpression when calling PutRule or defining an EventBridge Scheduler schedule, then target Lambda, SQS, Step Functions, or ECS. Hourly jobs in the wild include S3 partition registration for Athena, rotating short-lived tokens, snapshotting DynamoDB metrics into a reporting table, and re-driving messages that landed in a dead-letter queue during the previous hour. Many teams prefer this over rate(1 hour) purely because it lands on :00 and makes dashboards line up.
AWS-Specific Notes
- →The leading 0 is the minute field — EventBridge cron has no seconds field at all, unlike Quartz.
- →Fire times are 00:00 through 23:00 UTC on the hour; with an EventBridge Scheduler timezone setting they instead track the named zone, including DST shifts.
- →Hourly Lambda invocations from one rule total only about 730 per month, which is negligible cost on every target type.
Frequently Asked Questions
What is the difference between cron(0 * * * ? *) and rate(1 hour)?
Frequency is identical, but the cron form fires exactly on the hour while rate counts 60-minute intervals from schedule creation, so a rule made at 14:23 fires at :23 past each hour.
Why does the expression have six fields when Linux cron uses five?
EventBridge appends a year field and requires a ? in either day-of-month or day-of-week. Here the trailing * is the year and ? fills day-of-week.
0 * * * * in GitHub Actions
Minute pinned to 0 with the hour left open: 24 runs per day, one at the top of each UTC hour. On GitHub Actions this innocent-looking schedule has a catch — :00 is the most congested slot the scheduler has, so hourly-at-zero jobs see the worst delays of any common pattern.
GitHub Actions expression
0 * * * *Try it in the tester →At minute 0 of every hour UTC
Unix / POSIX equivalent: 0 * * * *
Field Breakdown
Using It with GitHub Actions
Declare it as on: schedule: - cron: "0 * * * *" in workflow YAML; remember the schedule only takes effect once that file is on the default branch. Hourly jobs cover stale-issue closers, artifact and cache cleanup, metric snapshots pushed to a data warehouse, and re-validation of external links in docs. GitHub's documentation explicitly suggests not scheduling on the hour because of load spikes — a one-character change to something like "23 * * * *" keeps the hourly rhythm and starts far more punctually.
GitHub-Specific Notes
- →Minute 0 is the single busiest scheduling moment on GitHub Actions; delays of 5-15 minutes at :00 are routine, and during incidents entire ticks can be skipped.
- →All 24 fire times are UTC. A job meant for the top of each local-business hour will be offset by your UTC difference and will shift when daylight saving changes — the schedule itself never moves.
- →Scheduled runs execute against the default branch's ref, so the workflow always checks out and runs your main-line code unless you explicitly check out another ref.
Frequently Asked Questions
Why is my hourly workflow consistently 10 minutes late?
Because :00 is the platform's rush hour. Every repo with a default-looking schedule fires then. Move the minute field to any non-round value and the delay typically shrinks dramatically.
Does 0 * * * * guarantee exactly 24 runs per day?
It requests 24, but the schedule event is best-effort — heavily loaded periods can skip a tick. If exactly-24 matters, have each run record a timestamp and backfill missed work.