All posts

Cron Job Hourly: Exact Syntax, Variations & Monitoring

September 14, 2026

The Cron Expression for Every Hour

The cron expression for a job that runs every hour is 0 * * * *. Five fields, each an asterisk except the first, which pins execution to minute zero.

The fields, left to right, are: minute, hour, day-of-month, month, day-of-week. The 0 fixes the minute; the four asterisks mean "any hour, any day, any month, any weekday." The job fires at 1:00, 2:00, 3:00, and so on, 24 times a day, always at the top of the hour.

This is the expression every hour that shows up in nearly every scheduler — cron, Kubernetes CronJobs, CI pipelines, hosted schedulers — because the syntax is POSIX-standard. No offsets, no ranges, no step values. Just a fixed minute and four wildcards.

The simplicity is also the trap. Because the expression is so easy to write correctly, teams rarely think hard about when within the hour it should run, or what happens once dozens of these jobs share the same trigger point. More on that shortly — first, the variations you'll actually need.

Common Hourly Variations

Most real-world hourly jobs aren't a bare 0 * * * *. Here are the patterns you'll copy-paste most often:

Goal Expression
Every hour, top of the hour 0 * * * *
Every hour, offset to :05 5 * * * *
Every hour, offset to :30 30 * * * *
Every 2 hours 0 */2 * * *
Every 3 hours 0 */3 * * *
Every 4 hours 0 */4 * * *
Every 6 hours 0 */6 * * *
Every 12 hours 0 */12 * * *
Business hours only, hourly (9am–5pm, Mon–Fri) 0 9-17 * * 1-5

The offset pattern matters more than it looks. Setting an offset minute — say 5 * * * * instead of 0 * * * * — spreads load away from the top of the hour, when a huge share of the internet's scheduled jobs fire simultaneously. It costs nothing and often fixes latency spikes downstream.

To run cron every 2 hours (or 3, 4, 6, 12), the */N step syntax in the hour field does the work: 0 */6 * * * fires at 00:00, 06:00, 12:00, and 18:00. Note that */N starts counting from hour 0, not from whenever you deployed the job — so */5 won't give clean 5-hour spacing across a 24-hour day (24 isn't divisible by 5), which trips people up more than any other part of this syntax.

A business hours cron schedule combines an hour range with a weekday range: 0 9-17 * * 1-5 runs hourly, on the hour, only between 9am and 5pm, Monday through Friday. That's the shape to use for jobs tied to office activity — report generation, sync jobs against systems only staffed on business days, and similar work that shouldn't run at 3am on a Saturday.

Why Hourly Jobs Break in Ways Daily Jobs Don't

A daily job fails once a day, at a time you probably chose deliberately, off-peak. An hourly job fails on a schedule shared by half the internet, twenty-four separate times, and that shared cadence introduces three risks daily jobs rarely see.

Thundering herd at the top of the hour. Because 0 * * * * is the default almost everyone reaches for, an enormous number of unrelated jobs — across your own infrastructure and everyone else's — fire in the same 60-second window. This load spike can saturate a shared database, rate-limited API, or downstream queue right when your job needs it most, causing timeouts that look like application bugs but are really scheduling collisions. Offsetting to :05 or :15 instead of :00 is a cheap, underused fix.

DST skip or double-fire. Twice a year, the hour that "doesn't exist" or "exists twice" during daylight saving transitions can cause an hourly job to either skip a run or fire twice in the same wall-clock hour, depending on how the scheduler resolves local time. This is a narrow but real failure mode worth understanding in depth — see Cron Job Time Explained: Clocks, Timezones, and DST Bugs for the mechanics across different scheduler implementations.

Overlapping runs. An hourly job assumes each execution finishes well within its 60-minute window. When a run takes longer than expected — a slow upstream API, a growing dataset, a locked table — the next scheduled run can start before the previous one exits. Without a lock file or mutex, two instances of the same job now touch the same data concurrently. This is one of the most common causes of duplicate records, double-sent notifications, and corrupted state in production systems, and it's specific to short-interval schedules; a daily job rarely runs long enough to collide with its own next execution.

The Real Risk: 24 Silent Chances to Fail Per Day

None of the above matters much if you'd notice immediately when a run fails. The trouble is, hourly jobs are exactly the kind of thing nobody watches closely. A daily job tends to get a glance in the logs the morning after. A job that runs 24 times a day becomes background noise — routine, repetitive, easy to stop consciously checking.

That repetition cuts both ways. A silent failure on an hourly job is statistically more likely simply because there are 24 chances a day for something to go wrong instead of one. And when it does fail, the cost compounds fast: a sync job that silently stops at 2am might not be noticed until the 11am business-hours check, by which point nine hours of data are missing, not one.

This is the core argument for hourly cron monitoring specifically. It's not that hourly jobs are more fragile than daily ones — it's that the failure mode is quieter and the blast radius grows every 60 minutes it goes unnoticed. A missed-job alert that arrives within minutes is a minor incident. The same alert arriving twelve hours later is a data-recovery project.

Monitoring an Hourly Cron Job in One Line

Heartbeat monitoring fixes this without changing your job's logic. The idea: your existing hourly command pings a monitoring URL when it completes, and if that ping doesn't arrive within the expected interval plus a grace period, you get alerted.

0 * * * * /path/to/your/script.sh && curl -fsS https://cronevra.com/ping/your-monitor-id

For an hourly job, the expected interval is naturally one hour, with a grace period — typically 5–10 minutes — that absorbs normal runtime variance without masking a real miss. If two consecutive expected pings don't arrive, that's a strong signal of a crashed job or an overlapping run stuck mid-execution, not just slow infrastructure.

To monitor an hourly cron job this way without building your own alerting stack, set it up directly with Cronevra — append the one-line ping to your crontab entry, define the hourly interval and grace period, and get notified the moment a run goes quiet instead of nine hours later. Check Pricing to see which plan fits your job volume, and turn those 24 daily chances to fail silently into 24 confirmed successes instead.

Frequently Asked Questions

What is the crontab syntax to run a job every hour?

The syntax is 0 * * * *, which runs a job at minute 0 of every hour, 24 times a day. The five fields represent minute, hour, day-of-month, month, and day-of-week, and setting only the minute to 0 while leaving the rest as wildcards produces the standard "every hour" schedule.

How do I run a cron job every 2 or 6 hours instead of every hour?

Use the step syntax in the hour field: 0 */2 * * * runs every 2 hours, and 0 */6 * * * runs every 6 hours. These step values start counting from hour 0, so intervals that don't divide evenly into 24 (like 5) won't produce consistent spacing across the day.

Why do all my hourly cron jobs seem to run at the exact same time?

They likely all use the default 0 * * * * expression, which fires at minute 0 — the same instant thousands of unrelated jobs across the internet also trigger. Offsetting to a non-zero minute, such as 5 * * * * or 15 * * * *, spreads the load and avoids this thundering-herd effect.

Does an hourly cron job run twice or get skipped during daylight saving time?

It can do either, depending on the scheduler and how it resolves local time during the DST transition hour. Some systems skip the "missing" hour in spring, while others double-fire during the repeated hour in autumn — a mechanism explained in Cron Job Time Explained: Clocks, Timezones, and DST Bugs.

What happens if my hourly job takes longer than 60 minutes to finish?

Without a lock, the next scheduled run starts before the previous one exits, causing two instances to execute concurrently against the same data. This overlap is a common source of duplicate records and corrupted state, and it's a risk specific to short-interval schedules like hourly jobs rather than daily ones.

How do I know if an hourly cron job silently failed?

You need heartbeat monitoring: an external ping sent when the job completes, checked against an expected interval and grace period. If the ping doesn't arrive on schedule, the monitoring service alerts you immediately rather than leaving the failure to surface hours later during a manual check.