Skip to main content
Tools Harbor

Cron expression for every hour

The cron expression 0 * * * * runs a job every hour on the hour — at 00:00, 01:00, 02:00 and so on through every day. Twenty-four runs per day. The leading 0 is the minute, not a counter — it means “fire at minute zero of every hour”, which gives you exactly one run per hour aligned to the wall clock.

Quick reference

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

@hourly is a Vixie-cron shortcut that expands to 0 * * * * — it’s accepted by Linux crontab, GitHub Actions, anacron and Kubernetes CronJob. AWS EventBridge does NOT accept it; use the explicit form there.

Why does the expression read 0 * * * *?

Cron’s first field is minute, the second is hour. 0 in the minute field means “fire when the wall-clock minute is exactly 0”. The * in the hour field means “any hour”. Combine the two: fire at minute 0 of every hour — i.e. on the hour, every hour.

A common mistake is reading 0 * * * * as “every 0 minutes” or “0 hours”. It’s neither. To run every N hours you’d write 0 */N * * * (N=2 means every other hour). To run every N minutes you’d write */N * * * *.

Variations

ScheduleExpression
Every hour at :30 instead of :0030 * * * *
Every hour, business hours only0 9-17 * * *
Every hour, weekdays only0 * * * 1-5
Every hour, but not at midnight0 1-23 * * *
Every hour in AWScron(0 * * * ? *)

How do I use it on each platform?

Linux crontab:

0 * * * * /usr/local/bin/sync-data

Or equivalently:

@hourly /usr/local/bin/sync-data

Kubernetes CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: sync-data
spec:
  schedule: "0 * * * *"
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: sync
              image: my-org/sync-data:1.0
          restartPolicy: OnFailure

AWS EventBridge:

aws events put-rule \
  --name hourly-sync \
  --schedule-expression "cron(0 * * * ? *)"

EventBridge has a rate(1 hour) alternative — same effect, simpler syntax. Use cron() if you specifically need :00 alignment; rate() fires hourly from the moment the rule was created.

GitHub Actions:

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

Common mistakes

Reading 0 * * * * as “every 0 minutes” and getting confused. The first field is minute. 0 in minute = “at minute zero”. Pair it with * in hour and you get “at minute zero of every hour”.

Using @hourly in AWS EventBridge. AWS rejects it. Use cron(0 * * * ? *) instead.

Confusing 0 * * * * with * 0 * * *. The latter means “every minute, but only during hour 0” — i.e. 60 runs between 00:00 and 00:59, then nothing for the rest of the day. Common typo when you misremember field order.

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

Frequently asked questions

Why is it `0 * * * *` and not `1 * * * *`?
The first field is *minute*, not "every Nth time". `0 * * * *` means "fire at minute 0 of every hour" — so :00, :01-of-the-next-hour means the next firing is at the top of the next hour. `1 * * * *` would run at :01 of every hour, not "every 1 hour".
What does `@hourly` mean and where does it work?
`@hourly` is a Vixie cron shortcut for `0 * * * *`. It works in Linux crontab, anacron, GitHub Actions and Kubernetes CronJob (kube-controller-manager parses it). It does NOT work in AWS EventBridge, which only accepts the explicit 6-field form. Use the explicit form when in doubt.
How do I run something every hour but not at :00?
Just change the minute. `30 * * * *` runs at :30 of every hour. `15 * * * *` runs at :15. The minute field is purely the wall-clock minute, not an offset from anything.

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