Cron expression for every 12 hours
The cron expression 0 */12 * * *
runs on the hour, every 12 hours. The restricted fields are minute 0 and hour */12; the remaining asterisks match anything. That works out to 2 runs per day.
0 */12 * * * Field breakdown
| Field | Value | Meaning |
|---|---|---|
| Minute | 0 | The top of the hour (:00) |
| Hour | */12 | Every 12 hours, counted from 0: 0, 12 |
| 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 0 */12 * * * — 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 "On the hour, every 12 hours": 2 runs per day. The runs land at 00:00, 12:00. Twenty-four divides by 12 without a remainder, so the spacing survives midnight: the gap from the 12:00 run to the next day's 00:00 run is the same 12 hours as every other gap.
When to use every 12 hours
Twice-daily runs suit data exports, morning-and-evening digests, and maintenance heavy enough that you want it split across the day rather than concentrated in one nightly window. Two runs is few enough that each failure is worth looking at individually rather than in aggregate. The catch is placement: a step of 12 is locked to 00:00 and 12:00, which on a UTC-based scheduler can put both runs at inconvenient local hours. When the times matter to a human reader, list the hours explicitly instead of stepping.
Common mistakes with every 12 hours
The step locks the runs to 00:00 and 12:00 and there is no way to move them while keeping the step, which is where most twelve-hour schedules go wrong: someone wants 06:00 and 18:00, writes 0 */12 * * * anyway, and the two runs land in the wrong halves of the day. Use the list form when the placement matters. The other failure is treating the pair as interchangeable. A midnight run and a noon run see very different system load and very different volumes of data, and the job that finishes comfortably at 00:00 can time out at 12:00.
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: '0 */12 * * *'
workflow_dispatch:
jobs:
scheduled:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run job
run: echo "Running scheduled job" - • Workflows scheduled at the top of the hour may be delayed during high-load periods. Consider offsetting the minute field (e.g. "5 */12 * * *") for more reliable timing.
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-cron-job
spec:
schedule: "0 */12 * * *"
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": "0 */12 * * *"
}
]
} - • 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
0 */12 * * * /path/to/command
# With logging:
0 */12 * * * /path/to/command >> /var/log/cron.log 2>&1 Variations
-
0 0,12 * * *At 12:00 AM and 12:00 PM The identical pair of hours as a list. Same result, and it can be edited to any two hours without abandoning the form. -
0 6,18 * * *At 06:00 AM and 06:00 PM The same twelve-hour rhythm moved to 06:00 and 18:00. A step cannot express this; only the list form can move the pair off midnight. -
0 */6 * * *On the hour, every 6 hours Four runs a day instead of two. The step up when twelve hours of accumulated work is more than one invocation can chew through. -
0 */12 * * 1-5On the hour, every 12 hours, Monday through Friday Twice a day on weekdays only, cutting fourteen runs a week to ten. The weekend gap reaches 60 hours, which the job has to survive.
Frequently asked questions
How do I read the cron expression 0 */12 * * *?
Read the five space-separated fields left to right as minute, hour, day of month, month, and day of week. In 0 */12 * * * the minute is 0, hour is */12, 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 "On the hour, every 12 hours", and a scheduler that checks all five fields once a minute will start the job 2 runs per day.
What timezone does 0 */12 * * * run in?
The schedule repeats through the day at fixed hours — 00:00, 12:00 — and which part of the day those hours fall in depends entirely on the scheduler. Moving between zones does not change how often the job runs; it changes when. GitHub Actions defaults to UTC and takes an optional timezone key next to cron, so the hours can be anchored to an IANA zone. Vercel Cron always evaluates in UTC, with no setting to change it. Kubernetes CronJobs use the controller's local zone unless spec.timeZone is set, and a CRON_TZ prefix inside the schedule string is rejected outright. A crontab follows the host clock unless CRON_TZ overrides it. In any zone that observes daylight saving, the whole set of run hours shifts by one twice a year.
How do I shift 0 */12 * * * away from midnight and noon?
A step in the hour field always counts from hour 0, so */12 is locked to 00:00 and 12:00. To move the pair, drop the step and list the hours you want: 0 6,18 * * * runs at 06:00 and 18:00, and 0 3,15 * * * runs at 03:00 and 15:00. Any two hours twelve apart keep the same cadence. This matters most on Vercel Cron, which is UTC-only, and on any GitHub Actions workflow that has not set the optional timezone key: midnight UTC may fall in the middle of your working day, and the pair of runs lands nowhere near where the schedule name suggests.
Related cron schedules
-
0 * * * *Every hour -
0 */2 * * *Every 2 hours -
0 */3 * * *Every 3 hours -
0 */4 * * *Every 4 hours -
0 */6 * * *Every 6 hours -
0 0 * * *Every day at midnight
Build a custom schedule in the cron expression generator, or browse every epochkit tool.