Cron expression for On the 15th of every month
0 0 15 * *
Runs once a month, at midnight on the 15th.
Next 5 runs (your local time)
These are shown in your browser’s timezone. The job itself runs in the scheduler’s timezone — often UTC — so the real run time can differ.
What people actually schedule with 0 0 15 * *
- Mid-cycle billing runs for subscriptions that bill on the 15th rather than the 1st, smoothing your payment processor load away from the month-start spike
- Generating a mid-month financial or KPI checkpoint report so leadership sees how the month is tracking before it's over
- Sending dunning reminders or payment retries to customers whose net-15 invoices come due halfway through the month
Use 0 0 15 * * on your platform
It’s the same 5-field expression everywhere — what changes is where you put it and which timezone it runs in.
Linux / crontab
0 0 15 * * /path/to/your-command
Runs in the server’s local timezone — check it with timedatectl.
Full field reference: crontab(5) man page.
GitHub Actions
on:
schedule:
- cron: "0 0 15 * *"
GitHub Actions always runs scheduled jobs in UTC — there is no timezone setting, and runs can be delayed under load (official docs).
Kubernetes CronJob
spec:
schedule: "0 0 15 * *"
Defaults to UTC. Set spec.timeZone (Kubernetes 1.27+)
for a specific zone — see the
CronJob docs.
Quartz / Spring @Scheduled
Quartz uses 6 fields (seconds first): 0 0 0 15 * *. Watch out:
Quartz day-of-week is 1=SUN … 7=SAT (not 0–6), and day-of-month /
day-of-week use ? — double-check if your schedule touches those fields
(Quartz cron reference).
Gotchas with on the 15th of every month schedules
- The 15th is the safe day-of-month — don't drift it to 28-31.
0 0 15 * *fires every month because the 15th always exists, unlike0 0 31 * *which silently skips February, April, June, September, and November. If you ever need a true "last day," use0 0 28-31 * *with a[ "$(date +%d -d tomorrow)" = "01" ]guard instead. - Midnight on the 15th lands in your server's timezone, not your finance team's. If cron runs in UTC and your billing cutoff is America/Los_Angeles, this fires at 4pm or 5pm on the 14th locally — a full calendar day early for west-coast customers. Pin it with
CRON_TZ=America/Los_Angelesor have the script re-check the local date before acting. - A monthly run that fails fails silently for 30 days. Unlike a daily job you'd notice broken by tomorrow, a missed 15th won't resurface until next month. Make the command exit non-zero on failure and pipe it to an alert (
|| curl -fsS https://hc-ping.com/your-uuid/fail) so a dead-letter run pages you the same night.
Will you know if this job silently fails?
Cron jobs fail quietly — a server reboots, a path changes, or an error code is ignored — and nobody notices until the data is missing. A cron monitor (a dead-man’s-switch) alerts you when a scheduled job does not check in on time.
Monitor your cron jobs with UptimeRobot →
Disclosure: this is an affiliate link — we may earn a commission if you sign up, at no extra cost to you.
Is 0 0 15 * * the right schedule?
If your real goal is "twice a month" cash-flow smoothing, pair this with the 1st using the 1st and 15th instead. If you actually mean "around the middle but tied to business days," cron can't do "15th business day" — gate every weekday with a day-counter in your script.
Or use the interactive cron generator & explainer, read the complete cron syntax guide, or pick another common schedule: