Cron Every 15 Minutes, 09:00–17:00 Weekdays: */15 9-17 * * 1-5
Every 15 minutes, every hour from 9:00 a.m. to 5:00 p.m., on weekdays. On the days it matches that means 36 runs per day, or roughly 9,396 a year. Below are the field-by-field reading, the exact minutes it lands on, and ready-to-paste blocks for crontab, Kubernetes and GitHub Actions.
The five fields, one at a time
Cron reads left to right, from the finest unit to the coarsest. This expression fills them like so:
| Field | Token | What it means |
|---|---|---|
| Minute | */15 | every 15 minutes — 0, 15, 30, 45 |
| Hour | 9-17 | 9, 10, 11, 12, 13, 14, 15, 16, 17 |
| Day of month | * | every day of the month |
| Month | * | every month of the year |
| Day of week | 1-5 | Monday, Tuesday, Wednesday, Thursday, Friday |
The exact minutes it fires
Because the minute field resolves to 4 specific values, each hour has 4 trigger points: :00, :15, :30, :45.
Everything in this schedule is anchored to the top of the hour. If the box also runs other jobs at :00, shift this one with a range like 2-59/5 instead of */5 — same cadence, no thundering herd.
Paste it in
*/15 9-17 * * 1-5 /usr/bin/env bash /opt/jobs/weekdays-every-15-minutes.sh >> /var/log/weekdays-every-15-minutes.log 2>&1apiVersion: batch/v1
kind: CronJob
metadata:
name: weekdays-every-15-minutes
spec:
schedule: "*/15 9-17 * * 1-5"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: weekdays-every-15-minutes
image: alpine:3.20
command: ["/bin/sh", "-c", "echo running weekdays-every-15-minutes"]on:
schedule:
- cron: '*/15 9-17 * * 1-5'
jobs:
run:
runs-on: ubuntu-latest
steps:
- run: echo "running weekdays-every-15-minutes"Before you ship it
- Absolute paths only. Cron does not read your shell profile, so relative commands and
$PATHlookups fail silently. - Stars mean "every value", not "ignore the field". A
*still participates in matching; it just matches everything. - Overlap protection. A run that takes longer than the gap will stack. Add
flock -nfor shell jobs, orconcurrencyPolicy: Forbidin Kubernetes. - Log something. Cron mails output to a mailbox nobody reads; redirect to a file and rotate it.
Frequently asked
What is the cron expression for every 15 minutes, 09:00–17:00 weekdays?
It is */15 9-17 * * 1-5. The engine reads it as: Every 15 minutes, every hour from 9:00 a.m. to 5:00 p.m., on weekdays. On a day the schedule matches, it fires 36 times, and across a calendar year it fires about 9,396 times.
How many times a day does every 15 minutes, 09:00–17:00 weekdays run?
On the days it is allowed to run, 36 times: 4 trigger points per hour across 9 hours of the day. Because the schedule matches 261 days in a non-leap year, the yearly total is 9,396.
Why does the job still fail with */15 9-17 * * 1-5 in crontab?
Almost always the environment, not the schedule. Cron runs with a near-empty PATH and no shell profile, so node, python3 or docker resolve to nothing. Use absolute paths, and redirect both streams (>> /var/log/job.log 2>&1) so a silent failure leaves a trace.
Does every 15 minutes, 09:00–17:00 weekdays still fire during a daylight-saving change?
Yes, with a caveat. When clocks spring forward the skipped wall-clock times simply do not occur that day, and when they fall back an hourly job can run twice. For jobs where a double run is harmful, guard the body with a lock file or an idempotency key rather than relying on cron to fire exactly once.