Cron expression for twice a day
The cron expression 0 0,12 * * *
runs at 12:00 AM and 12:00 PM. The restricted fields are minute 0 and hour 0,12; the remaining asterisks match anything. That works out to 2 runs per day.
0 0,12 * * * Field breakdown
| Field | Value | Meaning |
|---|---|---|
| Minute | 0 | The top of the hour (:00) |
| Hour | 0,12 | 00:00 (midnight) and 12:00 (noon) |
| 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 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 "At 12:00 AM and 12:00 PM": 2 runs per day. The runs land at 00:00, 12:00, because a comma-separated list matches each value it names and nothing between them.
When to use twice a day
Two runs a day cover the large middle ground between hourly polling and a single nightly batch: a morning and an evening sync, a twelve-hour backup rotation, digests at the start and the middle of the day. Writing the hours as a list rather than a step is the whole point — it lets the two runs sit wherever they are actually useful, twelve hours apart or not, and it states the times plainly for whoever reads the crontab after you. Reach for it whenever one daily run leaves too large a gap.
Common mistakes with twice a day
Two runs a day invite an assumption that rarely holds: that they are equivalent. The midnight run usually processes a full, quiet, closed period; the noon run processes half a day against live traffic. Sizing and timeouts derived from one will not necessarily hold for the other. The expression-level mistake is a stray space — 0 0, 12 * * * is not a valid five-field expression, and cron's error reporting for it is famously unhelpful. Keep lists tight, with no spaces around the commas. And note that Vercel Cron rejects this schedule outright on the Hobby plan, which allows one run per day.
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 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 0,12 * * *") for more reliable timing.
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-cron-job
spec:
schedule: "0 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 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 0,12 * * * /path/to/command
# With logging:
0 0,12 * * * /path/to/command >> /var/log/cron.log 2>&1 Variations
-
0 */12 * * *On the hour, every 12 hours The identical schedule written as a step. Shorter, but locked to 00:00 and 12:00 — it cannot express any other pair. -
0 6,18 * * *At 06:00 AM and 06:00 PM The same twelve-hour spacing shifted six hours, which puts both runs at a civilised local hour in more of the world than midnight and noon do. -
0 9,17 * * 1-5At 09:00 AM and 05:00 PM, Monday through Friday Bookends the workday, weekdays only. The two runs are eight hours apart, not twelve, which no step value can express. -
0 0,8,16 * * *At 12:00 AM, 08:00 AM and 04:00 PM Three runs a day at eight-hour spacing. The step up when twelve hours between runs leaves too much work to do in one.
Frequently asked questions
How do I read the cron expression 0 0,12 * * *?
Read the five space-separated fields left to right as minute, hour, day of month, month, and day of week. In 0 0,12 * * * the minute is 0, hour is 0,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 comma builds a list of individual values. Put together, cronstrue reads the whole thing as "At 12:00 AM and 12:00 PM", and a scheduler that checks all five fields once a minute will start the job 2 runs per day.
What timezone does 0 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 run a cron job twice a day at custom hours?
Put both hours in the hour field as a comma-separated list and leave the minute pinned: 0 0,12 * * * runs at midnight and noon, and 0 9,17 * * * runs at 09:00 and 17:00. A list accepts any hours, so the two runs do not have to be twelve apart — 0 8,14 * * * is equally valid. Reach for a step such as */12 only when the interval divides 24 evenly and starting at hour 0 is acceptable. Lists are the portable choice: every scheduler that reads five-field cron supports them.
Related cron schedules
-
0 0 * * *Every day at midnight -
0 9 * * *Every day at 9 AM -
0 12 * * *Every day at noon -
0 0 * * 1Every Monday -
0 0 * * 5Every Friday -
0 0 * * 1-5Every weekday
Build a custom schedule in the cron expression generator, or browse every epochkit tool.