All posts

Cron Every 4 Hours: Exact Syntax, Variations & Pitfalls

August 14, 2026

The Cron Expression for Every 4 Hours

The cron expression you want is:

0 */4 * * *

Drop that into your crontab and the job fires at 00:00, 04:00, 08:00, 12:00, 16:00, and 20:00, server time, every day — a clean six-run daily schedule anchored to midnight.

If you need to build or edit the crontab file itself rather than just the schedule string, see How to Generate a Crontab File the Right Way. If all you needed was the expression, you already have it. The rest of this article covers the variations and failure modes a bare syntax answer won't warn you about.

Breaking Down the Syntax

A cron expression has five fields, in order: minute, hour, day-of-month, month, and day-of-week. In 0 */4 * * *:

  • Minute (0) — run at the top of the hour, not partway through it.
  • Hour (*/4) — run on every 4th hour.
  • Day-of-month (*) — no restriction, every day.
  • Month (*) — no restriction, every month.
  • Day-of-week (*) — no restriction, every day of the week.

The part people misread is the hour field. */4 doesn't mean "4 hours from whenever the job was last triggered." It means "every hour value where hour mod 4 equals 0" — so 0, 4, 8, 12, 16, 20. It's a step value applied against a fixed 24-hour clock, not a rolling timer. That distinction matters the moment you add an offset or combine it with other fields, which is where a lot of confusion — and quietly wrong schedules — starts.

Common Variations You Might Actually Need

The plain "every 4 hours from midnight" schedule isn't always what a job needs. A few variants come up constantly.

Cron every 4 hours starting at a specific time. If you want runs at 1am, 5am, 9am, 1pm, 5pm, and 9pm instead of starting at midnight, list the exact hours instead of using a step value:

0 1,5,9,13,17,21 * * *

This gives you the same four-hour cadence, just offset. It's the right approach any time the business wants runs at, say, 8am instead of midnight — */4 alone can't produce an arbitrary starting hour, only multiples of 4 from 0.

Cron every 4 hours, business hours only. If a job only needs to run during a working day — say 9am to 5pm — enumerate hours directly:

0 9,13,17 * * *

or use a range with a step, 0 9-17/4 * * *, though listing hours explicitly is easier to read and audit later.

Crontab every 4 hours, weekdays only. Add a day-of-week restriction (1–5 for Monday–Friday) to skip weekends:

0 */4 * * 1-5

Each of these is a small tweak to the same base pattern, but they solve different real scheduling requirements — don't assume */4 alone covers all of them.

Mistakes That Quietly Break This Schedule

A syntactically valid expression can still run at the wrong time, or not reliably at all. Three issues account for most reports of cron every 4 hours not running on time.

Timezone mismatch. Cron typically runs in the server's system timezone, not the timezone your team assumes. Deploying a container set to UTC while your team plans schedules in Eastern time, for instance, shifts every run by several hours without triggering any error. If the job's "9am" runs feel off, check the system timezone before touching the expression.

Confusing */4 with a rolling interval. As covered above, */4 in the hour field always resets from 0, not from deployment time or last run. Combine it carelessly with day-of-month or month restrictions, and you can end up with unexpected gaps — for example, a day-of-month filter that excludes the very day a run was expected. Test combined expressions against a cron schedule calculator before trusting them in production.

Overlapping long-running jobs. If a job occasionally takes longer than 4 hours to finish — a spike in data volume, a slow downstream API — the next scheduled run can start while the previous one is still working. Depending on how the job is written, this produces duplicate processing, race conditions, or resource contention that looks like a scheduling problem but is really a concurrency problem. Locking or a run-in-progress check protects against this.

Confirming the Job Actually Ran — Not Just Scheduled

Here's the gap that pure syntax guides skip: a correct cron expression only guarantees cron attempted to trigger your job at the right time. It says nothing about whether the job actually ran, finished, or succeeded. Cron doesn't retry, doesn't alert on failure, and doesn't tell you when a job silently stopped firing altogether — say, after a server reboot, a crontab edit gone wrong, or a permissions change.

The first place to look is the log. See Crontab Log: Where to Find It and How to Read It for where cron writes execution records and how to read them. But logs have real limits — rotation policies can delete the evidence before you notice a gap, and a missing entry is easy to miss when nobody's actively watching for it. Cron Log Explained: Location, Format, and Limits goes deeper into where log-based debugging falls short, especially for infrequent jobs like a 4-hour schedule where a missed run might not surface for days.

This is exactly the blind spot cron job monitoring is built to close. Instead of manually checking logs after the fact, a monitoring layer expects a "check-in" at each scheduled run and alerts you the moment one doesn't arrive — whether that's a missed trigger, a failed execution, or a job that hung mid-run. It's a different category from basic uptime checks; see Health Checking Explained — And Its Blind Spot for Cron for why standard health checks don't catch cron-specific failures like this.

A correct 0 */4 * * * line tells you the schedule is defined — nothing more. It doesn't tell you the job ran, succeeded, or is still running six months from now after a server migration nobody documented. Cronevra closes that gap by watching every scheduled run of your 4-hour job and alerting you the instant one is missed or fails, instead of leaving you to find out from a customer or a stale dashboard. Check Pricing if you're ready to put real monitoring behind your scheduled jobs.

Frequently Asked Questions

What does */4 mean in a cron hour field?

*/4 means "every hour where the hour value is evenly divisible by 4," producing runs at 0, 4, 8, 12, 16, and 20. It's a step value applied to the fixed 24-hour clock, not a countdown from whenever the job was deployed or last ran.

How do I run a cron job every 4 hours starting at a specific time like 8am?

Use 0 1,5,9,13,17,21 * * * (or your desired hours) listed explicitly instead of a step value. */4 can only start from hour 0, so any custom starting hour requires enumerating the exact hours you want.

Can I run a cron job every 4 hours but only on weekdays?

Yes — add 1-5 to the day-of-week field: 0 */4 * * 1-5. This keeps the 4-hour cadence Monday through Friday and skips weekend runs entirely.

Why does my every-4-hour cron job seem to skip or drift over time?

The most common causes are a timezone mismatch between the server and your expected schedule, a combined day-of-month or month restriction that unintentionally excludes certain runs, or a job that's still running when the next trigger fires. None of these show up as a cron syntax error — they're logical, not syntactic, issues.

Is 0 */4 * * * the same as running a job every 4 hours from when it was deployed?

No. 0 */4 * * * always fires at fixed clock hours — 0, 4, 8, 12, 16, 20 — regardless of when the job was deployed or first scheduled. It's not a rolling four-hour timer measured from any particular start point.

How do I know if my every-4-hour cron job failed silently?

Cron itself won't tell you — it doesn't alert on missed or failed runs by design. Checking log files is a starting point, but rotation and lack of active alerting mean a missed run can go unnoticed for days; dedicated cron monitoring that expects a check-in each scheduled run and alerts you the moment one is missing is the reliable fix.