Cron expression for every 30 minutes
The cron expression */30 * * * *
runs every 30 minutes. The restricted fields are minute */30; the remaining asterisks match anything. That works out to 48 runs per day.
*/30 * * * * Field breakdown
| Field | Value | Meaning |
|---|---|---|
| Minute | */30 | Every 30 minutes, counted from 0: 0, 30 |
| Hour | * | Every hour (0–23) |
| Day of month | * | Every day of the month (1–31) |
| Month | * | Every month (1–12) |
| Day of week | * | Every day of the week (0–6) |
Next runs
- —
- —
- —
- —
- —
Run times are calculated in your browser timezone. The scheduler that executes the job uses its own — always UTC on Vercel Cron, UTC by default on GitHub Actions, the host clock on crontab.
How this schedule works
Cron evaluates the five fields of */30 * * * * — minute, hour, day of month, month, day of week — against the clock once a minute and starts the job on any minute where all five match, which cronstrue reads as "Every 30 minutes": 48 runs per day. Within every hour it fires at :00, :30. Sixty divides by 30 without a remainder, so the gap between runs stays constant as the hour rolls over.
When to use every 30 minutes
Half-hourly runs suit work that is too heavy for a five-minute loop but too time-sensitive to leave until the next hour: report refreshes, batched notification flushes, and periodic cleanup such as a vacuum or a temp-file sweep. Forty-eight runs a day give each invocation a comfortable window to finish in. It is also a sensible first cut for a job whose real cost you have not measured yet — start at thirty minutes, watch the runtime for a week, then tighten or relax the interval once you have numbers to argue from.
Common mistakes with every 30 minutes
The mistake that surfaces most often at this cadence is a neighbouring one. */45 looks like it should be the 45-minute version of this schedule, and it is not: it fires at :00 and :45 and then restarts, so the gaps alternate between 45 and 15 minutes. Only steps that divide 60 behave uniformly. The second is treating half-hourly as too cheap to think about. Forty-eight runs a day against a metered API is 17,520 a year, and a job that has quietly grown to 25 minutes is five minutes away from overlapping itself.
Platform snippets
The same expression, ready to paste into the four schedulers that read five-field cron. The notes below each snippet are the ones that follow from this schedule; the cron expression generator lists every caveat for every platform.
GitHub Actions
name: Scheduled workflow
on:
schedule:
- cron: '*/30 * * * *'
workflow_dispatch:
jobs:
scheduled:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run job
run: echo "Running scheduled job" Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-cron-job
spec:
schedule: "*/30 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: job
image: my-image:latest
command: ["/bin/sh", "-c", "echo Running"]
restartPolicy: OnFailure Vercel Cron
Hobby plan limitation: cron jobs can only run once per day. Schedules more frequent than daily will fail to deploy on Hobby. Use Pro plan or above for higher frequency.
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"crons": [
{
"path": "/api/cron",
"schedule": "*/30 * * * *"
}
]
} - • Pro plan and above: invocations occur within the specified minute (e.g. "5 8 * * *" runs between 08:05:00 and 08:05:59).
crontab
# Edit your crontab: crontab -e
*/30 * * * * /path/to/command
# With logging:
*/30 * * * * /path/to/command >> /var/log/cron.log 2>&1 Variations
-
0,30 * * * *At 0 and 30 minutes past the hour The same two fire minutes written out. At only two values, the list is arguably clearer than the step and costs three characters. -
15,45 * * * *At 15 and 45 minutes past the hour The same half-hourly cadence moved to a quarter past and a quarter to, which keeps the job clear of the :00 and :30 crowd on a shared host. -
*/45 * * * *Every 45 minutes A cautionary counterexample. It fires at :00 and :45 only, then restarts — 45 does not divide 60, so the gaps alternate between 45 and 15 minutes. -
*/30 * * * 1-5Every 30 minutes, Monday through Friday Half-hourly on weekdays only, cutting 336 runs a week down to 240. The weekend gap is a real one, so the job must tolerate a 62-hour pause.
Frequently asked questions
How do I read the cron expression */30 * * * *?
Read the five space-separated fields left to right as minute, hour, day of month, month, and day of week. In */30 * * * * the minute is */30, hour is *, day of month is *, month is *, day of week is *. Of the operators cron defines, this expression uses only the ones that matter here: an asterisk matches every value in its field and a slash introduces a step counted from the start of the field. Put together, cronstrue reads the whole thing as "Every 30 minutes", and a scheduler that checks all five fields once a minute will start the job 48 runs per day.
What timezone does */30 * * * * run in?
Less than you would think. Because the hour field is unrestricted, the cadence is the same in every timezone — 48 runs per day, wherever the scheduler's clock is set. The zone changes the labels on the run times rather than the schedule. It still matters at the edges. GitHub Actions evaluates schedules in UTC unless the workflow adds the optional timezone key beside cron; Vercel Cron is UTC-only; a Kubernetes CronJob follows the kube-controller-manager's zone unless spec.timeZone is set; and a crontab follows the host clock or CRON_TZ. The edge that bites is daylight saving: in a zone that shifts its clocks, the spring-forward hour never arrives and the fall-back hour arrives twice, so a schedule like this one loses or repeats a whole block of runs on those two days a year. Scheduling in UTC avoids it.
Why does */45 not run every 45 minutes?
Step values apply inside a single field, and the minute field only spans 0 to 59. The expression */45 therefore matches minutes 0 and 45, then the hour rolls over and the sequence restarts at minute 0 — the gaps alternate between 45 and 15 minutes rather than staying at 45. Steps that divide 60 evenly (2, 3, 5, 6, 10, 12, 15, 20, 30) are the only ones that produce a uniform cadence in the minute field. For an interval that is not a factor of 60, use a scheduler with real interval support or trigger the work from a long-lived worker.
Related cron schedules
-
* * * * *Every minute -
*/5 * * * *Every 5 minutes -
*/10 * * * *Every 10 minutes -
*/15 * * * *Every 15 minutes -
0 * * * *Every hour -
0 */2 * * *Every 2 hours
Build a custom schedule in the cron expression generator, or browse every epochkit tool.