Skip to main content
Tools Harbor

Cron expression for every 4 hours

The cron expression 0 */4 * * * runs a job every 4 hours, on the hour — at 00:00, 04:00, 08:00, 12:00, 16:00 and 20:00 every day, six times in 24 hours. The same syntax works in Linux crontab, Kubernetes CronJob and GitHub Actions. AWS EventBridge needs the 6-field form: cron(0 */4 * * ? *).

Quick reference

PlatformExpression
Unix / Linux crontab0 */4 * * *
Kubernetes CronJob0 */4 * * *
GitHub Actions0 */4 * * *
AWS EventBridge Rules / Schedulercron(0 */4 * * ? *)
Quartz (Java)0 0 */4 ? * *

What does “every 4 hours” actually mean?

The expression fires on the hour at 00:00, 04:00, 08:00, 12:00, 16:00 and 20:00. Six runs per 24 hours. The */4 in the hour field means “every 4 hours starting from 0”, and the leading 0 in the minute field pins the trigger to exactly the hour.

It does NOT mean “4 hours after the previous run finished”. If your job sometimes takes longer than 4 hours, the next run starts on schedule and overlaps — see Common mistakes below.

Compared to every 2 hours (0 */2 * * *), the 4-hour cadence cuts daily fires from 12 to 6 — useful for jobs with real per-run cost (full snapshots, expensive ETL, cross-region replication) where the extra coverage of every-2-hours isn’t worth the resource doubling. It’s also wide enough that overlapping runs are uncommon in practice, but still close enough that long-running jobs can cause stacking — set concurrency policies explicitly for jobs that occasionally exceed 90 minutes.

Variations

ScheduleExpression
Every 4h, weekdays only0 */4 * * 1-5
Every 4h, business hours (08–20)0 8,12,16,20 * * *
Every 4h, offset by 1 hour (01, 05, 09, 13, 17, 21)0 1-23/4 * * *
Every 4h, on the half hour past (:30)30 */4 * * *
Every 4h in AWS, weekdays onlycron(0 */4 ? * MON-FRI *)

How do I use it on each platform?

Linux crontab:

0 */4 * * * /usr/local/bin/refresh-cache

Kubernetes CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: refresh-cache
spec:
  schedule: "0 */4 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: refresh
              image: my-org/cache-refresh:1.0

For 4-hour intervals on Kubernetes, concurrencyPolicy: Forbid is usually the right call — runs that take 4+ hours are common (data refreshes, ETL, full backups), and stacked pods can starve cluster resources.

AWS EventBridge (CLI):

aws events put-rule \
  --name refresh-cache \
  --schedule-expression "cron(0 */4 * * ? *)"

GitHub Actions:

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

GitHub Actions runs in UTC, so a 0 */4 * * * schedule fires at 00:00, 04:00, 08:00 UTC. Translate to your team’s timezone if the wall-clock alignment matters — there is no per-workflow timezone option.

Common mistakes

Confusing */4 in the hour field with */4 in the minute field. */4 * * * * runs every 4 minutes — 360 times per day, two orders of magnitude more than intended. Always pin the minute to a single value (0) when stepping the hour field.

Stacking long-running jobs. A 4-hour interval is often used for jobs that are themselves several hours long. Without concurrencyPolicy: Forbid (Kubernetes) or a serialization layer (Lambda reserved concurrency 1, SQS FIFO queue), stacked runs compound resource pressure during a slow patch.

Forgetting AWS’s 6-field requirement. cron(0 */4 * * *) is rejected. AWS needs cron(0 */4 * * ? *)? for dow and trailing * for year.

Mixed-timezone containers. If your container’s TZ differs from the cluster default, 0 */4 fires at clock-aligned times in the container’s timezone, not the host’s. For environments with mixed TZs, set TZ=UTC explicitly in the CronJob spec or container image. The Kubernetes CronJob schedule generator emits a complete manifest with timeZone, concurrencyPolicy: Forbid and startingDeadlineSeconds already configured, and the AWS EventBridge cron generator handles the 6-field wrapper plus IaC snippets.

For ready-to-paste expressions for other intervals, see common cron schedules, or build a custom expression with the Cron Expression Builder.

Frequently asked questions

Does `0 */4 * * *` start at midnight or at the next 4-hour mark?
`*/4` aligns to clock hours starting from `0`, so the schedule fires at exactly 00:00, 04:00, 08:00, 12:00, 16:00 and 20:00 — six times per day. There is no drift relative to wall-clock time, regardless of when the cron daemon started.
How do I run every 4 hours but offset by 1 hour (01:00, 05:00, 09:00…)?
Use `0 1-23/4 * * *` — fires at hour 1, then every 4 hours from there: 01:00, 05:00, 09:00, 13:00, 17:00, 21:00. Or list explicitly: `0 1,5,9,13,17,21 * * *`.
Why use a cron expression instead of `rate(4 hours)` in AWS EventBridge?
`rate(4 hours)` fires every 4 hours from when the rule was created, so a rule created at 03:17 fires at 07:17, 11:17, 15:17 etc. — the actual minute drifts with the creation timestamp. `cron(0 */4 * * ? *)` always fires at clock-aligned times. Use `cron` when you care about wall-clock alignment; `rate` when you don't.

Need a different schedule?

Build cron expressions for Unix, Kubernetes, AWS EventBridge and Quartz — with a human-readable description and the next 5 run times.

Open the Cron Expression Builder →

Related