DEVESSENTIALS

0 * * * * — What does this cron expression mean?

0 * * * * is the hourly cron job, identical to @hourly. The single 0 in the minute field is the whole schedule: every hour, the job fires when the clock reads :00. That is simple, but it also means your job competes with every other hourly job on the same machine, cluster or CI provider in that one minute.

In plain English

0 * * * *

Run the job once every hour, exactly on the hour (minute 0): 00:00, 01:00, 02:00 … 23:00.

Frequency: 1 run per hour, 24 per day. cronstrue reads it as “Every hour”.

Open 0 * * * * in the Cron Builder

Field-by-field breakdown

0

Minute

0–59

*

Hour

0–23

*

Day of month

1–31

*

Month

1–12

*

Day of week

0–6

  • Minute0 — only at minute 0, the top of the hour.
  • Hour* — every hour, 0 through 23.
  • Day of month* — every day of the month.
  • Month* — every month.
  • Day of week* — every day of the week.

Next run times

Example: next 5 runs

Example: if it is Monday, 5 Jan 2026, 09:07 UTC, the next runs of 0 * * * * (UTC) are:

  1. #1Mon, 5 Jan 2026, 10:00 UTC
  2. #2Mon, 5 Jan 2026, 11:00 UTC
  3. #3Mon, 5 Jan 2026, 12:00 UTC
  4. #4Mon, 5 Jan 2026, 13:00 UTC
  5. #5Mon, 5 Jan 2026, 14:00 UTC

Live times from your current clock replace this example once the page loads. Cron uses the scheduler's timezone; most cloud servers, GitHub Actions and AWS EventBridge rules use UTC.

0 * * * * in Quartz, AWS, GitHub Actions, Kubernetes and systemd

Crontab (Linux / macOS)

0 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1

Standard 5-field syntax used by Vixie cron, cronie, busybox crond and most libraries (node-cron, APScheduler, robfig/cron).

Quartz (6 and 7 fields)

0 0 * * * ?
0 0 * * * ? *

# Spring @Scheduled
@Scheduled(cron = "0 0 * * * *")

Generators such as CronMaker output 0 0 0/1 1/1 * ? * for “every hour”. It is the same schedule written verbosely: 0/1 is every hour from 0 and 1/1 is every day from the 1st.

AWS EventBridge

cron(0 * * * ? *)

# interval alternative
rate(1 hour)

Note the singular unit: rate(1 hour), not rate(1 hours). AWS rejects the plural when the value is 1.

GitHub Actions

on:
  schedule:
    # Every hour, evaluated in UTC
    - cron: '0 * * * *'

GitHub's documentation warns that load is highest at the start of every hour, and runs may be delayed or dropped. If “roughly hourly” is fine, prefer a random minute such as 23 * * * *.

Kubernetes CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: every-hour
spec:
  schedule: "0 * * * *"
  timeZone: "Etc/UTC"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 600
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: job
              image: alpine:3.20
              command: ["/bin/sh", "-c", "echo running"]

Kubernetes accepts @hourly too. A 10-minute deadline keeps a late Job useful without risking a pile-up.

systemd timer

# /etc/systemd/system/myjob.timer
[Unit]
Description=Every hour

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

hourly means *-*-* *:00:00. Add RandomizedDelaySec=5min if many timers share the same host.

When to use every hour

  • Rolling up raw events into hourly aggregates.
  • Refreshing exchange rates, weather or other data that changes on an hourly cadence.
  • Running log shippers or upload jobs that batch an hour's worth of files.
  • Pruning temporary files and caches before they fill a small disk.

Common pitfalls with 0 * * * *

:00 is the most crowded minute everywhere

Hourly jobs from every team, every package and every tenant land on :00. On shared databases this produces an hourly latency spike; on CI providers it means queueing. Jenkins introduced the H symbol (H * * * *) for exactly this reason: it picks a stable pseudo-random minute per job. In plain cron, pick a minute yourself.

# Everyone's hourly job
0 * * * *

# Same frequency, off the crowded minute
23 * * * *

Hourly is not “60 minutes after the last run”

Cron fires at wall-clock :00. If the server is down from 10:58 to 11:02, the 11:00 run is lost, not delayed. If a run must not be skipped, use a scheduler that catches up (systemd Persistent=true, anacron) or make the next run process everything since the last success.

0 * * * * vs * 0 * * *

Swapping the first two fields changes everything: * 0 * * * runs every minute between 00:00 and 00:59, 60 times a day, and never at any other hour.

Related cron expressions

Need a different schedule? Paste 0 * * * * into the Cron Builder and edit it, browse all cron expressions, read the cron syntax guide or copy from the cron examples list.

Frequently Asked Questions

What does 0 * * * * mean in cron?▾

Once every hour at minute 0. The job runs at 00:00, 01:00, 02:00 and so on, 24 times a day. It is identical to @hourly.

What is 0 0 0/1 1/1 * ? * in cron?▾

It is a 7-field Quartz expression (seconds, minutes, hours, day-of-month, month, day-of-week, year) meaning every hour on the hour, the same as 0 * * * * in crontab. Tools such as CronMaker generate this verbose form; 0 0 * * * ? is the shorter Quartz equivalent.

How do I write an hourly cron job for AWS?▾

Use cron(0 * * * ? *) to run at the top of every hour UTC, or rate(1 hour) to run every 60 minutes from when the rule was created.