Cron at a random time each day: jitter without a random field
No cron field produces a random time. The standard approach is to schedule at a fixed time and start the command with a sleep of a random number of seconds, which spreads a fleet of machines across a window.
At a glance
- Expression
- 0 3 * * * with a random sleep
- Fires
- Once a day, at a time that varies within a chosen window.
- Restricted fields
- Minute and hour pinned in the expression. The variation comes entirely from the command.
- Equivalent macro
- None. Every macro expands to a fixed time.
- Typical window
- A sleep of 0 to 3600 seconds spreads runs across the hour after the scheduled time.
- Why it matters
- A hundred machines on 0 3 * * * make one request each at exactly the same second.
In detail
Cron is deterministic by design. Every field matches a fixed set of values and there is no syntax that means pick a value at random, so a randomised start time has to come from the command rather than from the schedule. The pattern is to fire at a fixed time and have the first thing the command does be a sleep of a random length.
The reason to bother is fleet behaviour. One machine running a nightly job at 03:00 is unremarkable. Two hundred machines running the same job at 03:00, all pulling from the same package mirror or the same configuration service, is a spike that looks exactly like an attack to whatever is on the other end. Spreading them across an hour flattens it completely.
The shell matters here. Cron runs commands with a minimal shell, and on many systems that is not bash, so the RANDOM variable may not exist and the command silently sleeps for zero seconds. Using shuf to pick the number is portable across anything with GNU coreutils and fails loudly rather than quietly if it is missing.
One consequence worth planning for: the job no longer finishes at a predictable time. If anything downstream waits for its output, either give the downstream job a window that covers the whole jitter range, or have the job signal completion rather than having the next thing guess.
Examples
0 3 * * * sleep $(shuf -i 0-3600 -n 1); /opt/app/bin/nightly.sh >> /var/log/nightly.log 2>&1shuf comes with GNU coreutils. The job starts somewhere between 03:00 and 04:00.
0 3 * * * sleep $(shuf -i 0-300 -n 1); /opt/app/bin/nightly.sh0 3 * * * sleep $(( $(hostname | cksum | cut -d' ' -f1) \% 3600 )); /opt/app/bin/nightly.shEach host gets a stable offset, so the same machine always runs at the same time.
Things that catch people out
- The RANDOM variable is a bash feature. Cron often uses a plain shell where it expands to nothing, and sleep with no argument is an error.
- A sleeping process occupies a slot for the whole delay. On a machine with many jittered jobs that is a lot of idle processes.
- The host based variant is stable, which is usually better than random: the same machine runs at the same time every day, so a problem is reproducible.
Try it: Cron Expression Parser
Paste any five or six field expression to get a plain English description, a per field breakdown and the next run times in your local time.
Cron Expression Parser
LOCAL · Browser-OnlyParse cron expressions and see the next scheduled run times
Supports 5-field (minute hour day month weekday), 6-field (with seconds), named values (JAN, MON), and @macros (@daily, @hourly, ...)
Use the dropdowns below to build your cron expression visually, or type a custom value in each field.
Human-Readable Description
Every 5 minutes
Field Breakdown
| Field | Allowed | Value | Meaning |
|---|---|---|---|
| Minute | 0-59 | */5 | every 5 minute(s) |
| Hour | 0-23 | * | every hour |
| Day of Month | 1-31 | * | every day of month |
| Month | 1-12 | * | every month |
| Day of Week | 0-7 | * | every day of week |
Related cron expressions
All cron expressions →Cron every day at 2 AM: 0 2 * * * and daylight saving
0 2 * * * runs once a day at 02:00, the hour many timezones use for daylight saving transitions. What happens when 02:00 is skipped or repeated.
Cron every day at midnight: 0 0 * * *
0 0 * * * runs once a day at 00:00 and is what both @daily and @midnight expand to. The midnight pile up, and why 0 24 * * * is invalid.
Cron every day at 9 AM: 0 9 * * *
0 9 * * * runs once a day at 09:00 in the server timezone, which on most containers is UTC. How to find out and how to fix it.
Cron every 5 minutes: */5 * * * *
*/5 * * * * fires 288 times a day at minutes 0, 5, 10 and so on. Why every job on the box lands on the same minute and how 2-59/5 fixes it.
The cron asterisk: what * means in each field
An asterisk matches every value in a field's own range, which differs per field. Why * */1 * * * is every minute and not every hour.
The cron comma: listing several values in one field
A comma joins several values into one field, and each element can itself be a value, a range or a step. Spaces inside a list break the expression.
Tools for this
- Cron Expression Parser Free online cron expression parser - parse cron expressions and see the next scheduled run times
- Crontab Generator Free online crontab generator - build cron expressions visually with an intuitive interface