That cannot be entirely done with cron, but you could use this to get the desired result:
15,45 8-21 * * * sleep ${RANDOM:0:2}m ; /path/to/script.shCron only matches clock-aligned minutes, so “truly random” times within a window need a wrapper. The trick above schedules deterministic base times (every hour from 8 AM to 9 PM, at :15 and :45 — 28 runs) and adds a random delay before launching the actual script. ${RANDOM:0:2} yields a 1-2 digit number (0-99); sleep ...m waits that many minutes. Combined, each run fires somewhere in a ~99-minute jitter window after the scheduled minute.
The “20 times a day” ask is hard to satisfy exactly without a workflow engine. The pragmatic version: schedule slightly more than you need (e.g. 28 base slots) and have the script self-skip — keep a counter file, increment it, exit early once you've hit 20 runs. Or use a stochastic gate: [ $((RANDOM % 28)) -lt 20 ] || exit 0 gives ~20 runs per day on average.
Jenkins users have a cleaner option: the H hash modifier automatically jitters the minute deterministically per job. See the Jenkins cron guide for that pattern.
Read the dedicated guide: Cron every day.
View future cron matches in a calendar
Showing next 1000 cron schedules
See this cron expression on the calendar → view example
Other answers and worked examples for the most confusing cron expressions.
Ready to schedule it?
WordPress, Shopify, Railway, Cloud Run, Vercel, HubSpot, Ghost, your own box. If it answers HTTP, Crontap can drive it on a clock you can read, in the timezone that actually matters, and page you when something breaks.
Free forever tier ・ No credit card required
Schedule
“every 5 minutes”
Next run
in 23s
Permalink: https://tool.crontap.com/help/cron-job-run-script-at-random-times-between-two-hourly-intervals