Skip to main content
Tools Harbor

Cron expression for every 30 minutes

The cron expression */30 * * * * runs a job every 30 minutes — at :00 and :30 of every hour, 48 times per day. The standard 5-field syntax works in Linux crontab, Kubernetes CronJob and GitHub Actions. AWS EventBridge accepts the same */30 (or the equivalent 0/30) inside a 6-field expression: cron(*/30 * * * ? *).

Quick reference

PlatformExpression
Unix / Linux crontab*/30 * * * *
Kubernetes CronJob*/30 * * * *
GitHub Actions*/30 * * * *
AWS EventBridge Rules / Schedulercron(*/30 * * * ? *) (or cron(0/30 * * * ? *))
Quartz (Java)0 */30 * ? * *

What does “every 30 minutes” actually mean?

The expression fires on absolute wall-clock minutes — :00 and :30 — of every hour. Forty-eight runs per day. It’s clock-aligned, not relative to when the cron daemon started, so a daemon restart at 13:17 still fires the next run at 13:30, not 13:47.

*/30 and 0,30 are functionally identical. */30 is read as “every 30 minutes from 0”, which evaluates to 0,30. Use whichever reads better to your team — most modern cron parsers accept both.

The 30-minute interval is common for cache refreshes, sync jobs, health checks and external-API polling. It’s frequent enough that one missed run is recoverable on the next tick, and infrequent enough that even slow jobs (a few minutes each) rarely overlap. Compared to every 5 minutes (*/5 * * * *), 30-minute intervals trade timeliness for resource efficiency — 48 fires per day instead of 288. Compared to every hour (0 * * * *), it doubles coverage with almost no additional concurrency risk, since most 30-minute jobs are sub-30-minute in duration.

Variations

ScheduleExpression
Every 30 min, business hours (9 AM – 5 PM)*/30 9-17 * * *
Every 30 min, weekdays only*/30 * * * 1-5
Every 30 min, on quarters past instead (:15, :45)15,45 * * * *
Every 30 min, business hours weekdays only*/30 9-17 * * 1-5
Every 30 min in AWS, weekdays onlycron(*/30 * ? * MON-FRI *)

How do I use it on each platform?

Linux crontab:

*/30 * * * * /usr/local/bin/refresh-feeds

Kubernetes CronJob:

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

concurrencyPolicy: Forbid is recommended unless your job is reliably under 30 minutes — overlap doubles peak resource usage and can corrupt jobs that read or write shared state.

AWS EventBridge (CLI):

aws events put-rule \
  --name refresh-feeds \
  --schedule-expression "cron(*/30 * * * ? *)"

GitHub Actions:

on:
  schedule:
    - cron: '*/30 * * * *'

GitHub Actions enforces a 5-minute minimum granularity, so */30 is comfortably above the threshold. Be aware that scheduled GitHub Actions can be delayed under heavy platform load — don’t rely on exact :00/:30 firing for time-sensitive workflows.

Common mistakes

Misreading 30 * * * * as “every 30 minutes”. 30 * * * * fires once per hour at :30 — only 24 runs per day, half what you intended. Use */30 * * * * (or 0,30 * * * *) for the every-30-minute pattern.

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

Stacking on near-30-minute jobs. Jobs that take 25–35 minutes will sometimes overlap, sometimes not, sometimes for hours during a slow patch. Set concurrencyPolicy: Forbid (Kubernetes), use Lambda reserved concurrency 1 (AWS), or queue through SQS / Step Functions to serialize.

For the full AWS ruleset and CloudFormation/Terraform snippets, see the AWS EventBridge cron generator. For a complete CronJob YAML with concurrencyPolicy and startingDeadlineSeconds, use the Kubernetes CronJob schedule generator.

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

What's the difference between `*/30 * * * *` and `0,30 * * * *`?
Functionally identical — both fire at :00 and :30 of every hour. `*/30` is the step form (more concise); `0,30` is the explicit list form (more readable for new readers, and what some old cron implementations require). Modern Vixie cron, GNU mcron and BusyBox all accept both.
How do I run every 30 minutes during business hours only?
Combine the step minute with an hour range: `*/30 9-17 * * *` runs at :00 and :30 from 9 AM through 5 PM (last fire 17:30). Add a weekday range to limit further: `*/30 9-17 * * 1-5`.
Why does AWS EventBridge sometimes show `0/30` instead of `*/30`?
AWS EventBridge accepts both `*/30` and `0/30` for the minute field — they're equivalent. AWS documentation prefers the `0/N` form (start-from-0 syntax) for clarity, but `*/30` is also valid. The 6-field wrapper (`cron(...)` with year and `?` for one of dom/dow) is what matters.

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