The single mistake that trips up most people: */5 is not the same as 5. Drop the */ and you get 5 * * * *, which runs once an hour at minute 5 — a 12× difference in frequency from one missing character. It is the go-to schedule for monitoring, health checks, data synchronization, and cache warming — frequent enough to catch problems quickly without hammering your systems.
The rest of this article covers exactly how the step operator works (including the case where */N does not mean "every N minutes"), a full table of every common interval, the platforms that parse this expression differently, and how to keep a 5-minute job from tripping over itself.
Decoding */5 * * * *
The expression */5 * * * * breaks down into five fields. Only the minute field does the work; the rest are wildcards:
*/5- Minute field: Every 5 minutes*- Hour field: Every hour*- Day of month field: Every day*- Month field: Every month*- Day of week field: Every day of the week
The key is understanding the */5 syntax in the minute field.
*/5 vs 5: the one-character mistake
This is the trap worth burning into memory, because the two expressions look almost identical and behave nothing alike:
| Expression | Slash? | When it runs | Runs per day |
|---|---|---|---|
*/5 * * * * | step operator | Every 5 minutes (:00, :05, :10 … :55) | 288 |
5 * * * * | fixed value | Once an hour, only at minute 5 (1:05, 2:05 …) | 24 |
The slash (/) is what turns a value into an interval. Without it, a bare number is a single fixed minute. So 5 * * * * is not "every 5 minutes" — it is "at 5 minutes past every hour." If a job you expected to run constantly is only firing once an hour, a missing */ is the first thing to check.
How the Slash Operator Works
The slash (/) creates step values or intervals. When you write */5 in the minute field, you're telling cron:
"Start at the minimum value (0), then step by 5."
This means the task executes when the minute value is divisible by 5 with no remainder:
- Minute 0 (12:00, 1:00, 2:00, etc.)
- Minute 5 (12:05, 1:05, 2:05, etc.)
- Minute 10 (12:10, 1:10, 2:10, etc.)
- Minute 15 (12:15, 1:15, 2:15, etc.)
- Minute 20 (12:20, 1:20, 2:20, etc.)
- Minute 25 (12:25, 1:25, 2:25, etc.)
- Minute 30 (12:30, 1:30, 2:30, etc.)
- Minute 35 (12:35, 1:35, 2:35, etc.)
- Minute 40 (12:40, 1:40, 2:40, etc.)
- Minute 45 (12:45, 1:45, 2:45, etc.)
- Minute 50 (12:50, 1:50, 2:50, etc.)
- Minute 55 (12:55, 1:55, 2:55, etc.)
That's 12 times per hour, 288 times per day (24 hours × 12 executions).
What */5 Actually Means
The */5 pattern is shorthand for 0-59/5, which means "every 5 minutes within the range 0 to 59." The asterisk, in the words of the crontab(5) man page, "always stands for 'first-last'" — so it expands to the full range of the field, and the /5 then steps through that range.
The task doesn't run at random 5-minute intervals — it runs at predictable, clock-aligned times. If you start your cron job at 3:17 PM, it won't run at 3:17, then 3:22, then 3:27. Instead, it waits until the next 5-minute mark (3:20 PM), then runs at 3:20, 3:25, 3:30, and so on.
The Gotcha: */N Divides the Field, It Does Not Repeat Forever
This is the part that causes real incidents, and it is worth being precise about. A step value is evaluated only inside its own field, and the field restarts at its minimum when it rolls over. The crontab(5) man page says so directly:
"Please note that steps are evaluated just within the field they are applied to. For example
*/23in hours field means to execute the job on the hour 0 and the hour 23 within a calendar day."
Read that example carefully: */23 in the hour field is not "every 23 hours." It fires at hour 0 and hour 23 — a 23-hour gap, then a 1-hour gap, then repeat.
The same trap applies to minutes. */7 * * * * looks like "every 7 minutes." It is not:
*/7 * * * * → :00 :07 :14 :21 :28 :35 :42 :49 :56 then :00 of the next hour
That is 9 runs per hour, and the gap between the last run at :56 and the next at :00 is 4 minutes, not 7. The interval is uneven forever.
*/5 gets away with it purely because 60 is divisible by 5. The last run is at :55 and the next is at :00 — exactly 5 minutes later, so the cadence really is uniform.
The rule: a minute step is evenly spaced only when it divides 60, and an hour step only when it divides 24.
| Field | Range | Step values that stay evenly spaced |
|---|---|---|
| Minute | 0-59 | 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 |
| Hour | 0-23 | 1, 2, 3, 4, 6, 8, 12 |
Anything else — */7, */8, */9, */11, */13, */25 on minutes, or */5, */7, */23 on hours — produces a short final gap at every rollover. If you genuinely need "every 7 minutes" with a constant interval, cron cannot express it; use a systemd timer with OnUnitActiveSec=7min, a scheduler that supports true rate expressions, or have the job schedule its own next run.
A second, related trap: stepping the hour field while leaving the minute field as *.
* */2 * * * wrong — fires every minute, for a full hour, every 2 hours (720 runs/day)
0 */2 * * * right — fires once, at the top of every second hour (12 runs/day)
Whenever you put a step on the hour, pin the minute to a fixed value.
Common Use Cases for Every 5 Minutes
Running tasks every 5 minutes strikes a balance between frequency and system load. Here's where this pattern shines:
System Monitoring
Monitoring systems often check health status every 5 minutes:
*/5 * * * * /usr/local/bin/check-server-health.sh
This frequency catches problems quickly without overwhelming your monitoring infrastructure. For critical systems, you might go tighter (every minute), but for most applications, 5-minute intervals provide adequate responsiveness.
API Data Synchronization
Pulling data from external APIs on a regular cadence:
// Node.js with node-cron
cron.schedule('*/5 * * * *', async () => {
try {
const data = await fetchExternalAPI();
await saveToDatabase(data);
console.log('Data synced at', new Date());
} catch (error) {
console.error('Sync failed:', error);
}
});
This ensures your application has reasonably fresh data without hitting rate limits that might come from per-minute requests.
Cache Warming
Regenerating frequently-accessed cached data:
# Python with APScheduler
@scheduler.scheduled_job(CronTrigger.from_crontab('*/5 * * * *'))
def warm_cache():
popular_pages = get_popular_pages()
for page in popular_pages:
regenerate_cache(page)
Queue Processing
Checking for pending jobs in a work queue:
// Java Spring
@Scheduled(cron = "0 */5 * * * *") // Note: Spring uses 6 fields
public void processQueue() {
List<Job> pendingJobs = jobRepository.findPending();
for (Job job : pendingJobs) {
processJob(job);
}
}
Log Analysis
Aggregating or analyzing recent log entries:
*/5 * * * * /usr/local/bin/analyze-logs.py --last 5m
The 5-minute interval matches the time window being analyzed, ensuring continuous coverage without overlap.
Availability Checks
Testing website or service availability:
cron.schedule('*/5 * * * *', async () => {
const isUp = await checkServiceHealth('https://example.com');
if (!isUp) {
await sendAlert('Service is down!');
}
});
Common Interval Variations
While every 5 minutes is popular, other intervals serve different needs. Here is the full cheat sheet — minute-step and hour-step patterns side by side. Note the pattern for hours: you put the step on the hour field and pin the minute to 0, otherwise the job fires every minute inside each chosen hour.
| Interval | Cron expression | Fires at | Runs/day |
|---|---|---|---|
| Every minute | * * * * * | every minute | 1,440 |
| Every 2 minutes | */2 * * * * | :00, :02, :04 … :58 | 720 |
| Every 5 minutes | */5 * * * * | :00, :05, :10 … :55 | 288 |
| Every 10 minutes | */10 * * * * | :00, :10, :20 … :50 | 144 |
| Every 15 minutes | */15 * * * * | :00, :15, :30, :45 | 96 |
| Every 30 minutes | */30 * * * * | :00, :30 | 48 |
| Every hour | 0 * * * * | top of every hour | 24 |
| Every 2 hours | 0 */2 * * * | 00:00, 02:00, 04:00 … | 12 |
| Every 6 hours | 0 */6 * * * | 00:00, 06:00, 12:00, 18:00 | 4 |
| Every 12 hours | 0 */12 * * * | 00:00, 12:00 | 2 |
The individual variations below add context on when each one is the right choice:
Every Minute: * * * * *
* * * * *
The most frequent possible schedule—1,440 executions per day. Use sparingly:
- Critical system monitoring
- Real-time data processing
- High-frequency trading systems
- Urgent queue processing
Warning: Every-minute execution can strain resources. Ensure your task completes in under 60 seconds to avoid overlapping runs.
Every 2 Minutes: */2 * * * *
*/2 * * * *
Runs at minutes 0, 2, 4, 6, 8... 58 (30 times per hour). A middle ground between every minute and every 5 minutes for moderately urgent tasks.
Every 10 Minutes: */10 * * * *
*/10 * * * *
Runs at minutes 0, 10, 20, 30, 40, 50 (6 times per hour). Good for:
- Less critical monitoring
- Periodic data fetches
- Regular database maintenance
- Report generation
Every 15 Minutes: */15 * * * *
*/15 * * * *
Runs at minutes 0, 15, 30, 45 (4 times per hour). Common for:
- Scheduled backups during business hours
- Regular status updates
- Moderate-frequency synchronization
This interval aligns nicely with quarter-hours, making it easy to remember and communicate.
Every 30 Minutes: */30 * * * *
*/30 * * * *
Runs at minutes 0 and 30 (twice per hour). Popular for:
- Hourly-ish tasks that don't need precision
- Resource-intensive operations
- Less frequent data updates
Note: */30 is equivalent to 0,30 but more semantic—it clearly states "every 30 minutes."
Every Hour: 0 * * * *
0 * * * *
Runs at the top of every hour (XX:00). The next step up from minute-based intervals:
- Hourly reports
- Log rotation
- Cache clearing
- Scheduled emails
Restricting Intervals to Specific Times
The real power comes from combining interval patterns with time restrictions:
Every 5 Minutes During Business Hours
*/5 9-17 * * 1-5
Runs every 5 minutes, but only from 9 AM to 5 PM on weekdays. Perfect for:
- Business hours monitoring
- Support ticket checking
- API rate limit management (spread requests during work hours only)
Every 10 Minutes at Night
*/10 0-6 * * *
Runs every 10 minutes from midnight to 6 AM. Useful for:
- Off-hours maintenance
- Background processing during low traffic
- Database optimization
Every 15 Minutes on Weekdays
*/15 * * * 1-5
Every 15 minutes, but only Monday through Friday:
- Business day automation
- Weekday-only integrations
- Office hours tasks
Every 5 Minutes for a Few Hours
*/5 8-12 * * *
Every 5 minutes from 8 AM to noon:
- Morning rush monitoring
- Limited-time promotions
- Targeted processing windows
Performance Considerations
Running tasks every 5 minutes seems innocent, but 288 daily executions add up:
Execution Time
Ensure your task completes in under 5 minutes. If not, implement concurrency controls to prevent overlapping executions:
let isRunning = false;
cron.schedule('*/5 * * * *', async () => {
if (isRunning) {
console.log('Previous execution still running, skipping...');
return;
}
isRunning = true;
try {
await performTask();
} finally {
isRunning = false;
}
});
Resource Impact
Each execution consumes CPU, memory, network, or database resources. For 288 daily runs:
- Database queries should be optimized
- API calls should respect rate limits
- Logs should be manageable in volume
- Memory leaks compound quickly
Error Handling
With frequent execution, errors happen. Implement proper logging and alerting:
import logging
@scheduler.scheduled_job(CronTrigger.from_crontab('*/5 * * * *'))
def frequent_task():
try:
result = perform_operation()
logging.info(f'Task completed successfully: {result}')
except Exception as e:
logging.error(f'Task failed: {e}')
# Don't alert on every failure—implement threshold-based alerting
if consecutive_failures > 3:
send_alert(f'Task has failed 3+ times: {e}')
Rate Limiting
If your 5-minute task calls external APIs, verify the rate limit:
- 12 calls per hour
- 288 calls per day
- ~8,640 calls per month
Ensure this fits within API quotas. If not, consider less frequent intervals.
Alternatives to Fixed Intervals
Sometimes fixed 5-minute intervals aren't ideal:
Event-Driven Triggers
Instead of checking for new data every 5 minutes, have the data source trigger your task via webhooks or message queues. This eliminates unnecessary checks and provides instant response.
Exponential Backoff
For failure scenarios, consider increasing interval on repeated failures:
- First failure: Retry after 1 minute
- Second failure: Retry after 5 minutes
- Third failure: Retry after 15 minutes
- Fourth failure: Retry after 1 hour
Dynamic Scheduling
Adjust frequency based on load or time of day:
// Pseudo-code example
const interval = isDaytime() ? '*/5' : '*/30';
cron.schedule(`${interval} * * * *`, task);
Not Every Scheduler Parses */5 * * * * the Same Way
"Cron syntax" is not one syntax. The five-field expression above is standard Unix cron, and several popular schedulers accept something that looks similar but is not identical. Pasting a five-field expression into a six-field parser is a classic deploy-day failure.
| Platform | Fields | */5 * * * * valid? | Notes |
|---|---|---|---|
| crontab (Vixie/cronie) | 5 | Yes | The baseline. No seconds field. |
| Kubernetes CronJob | 5 | Yes | Standard cron. .spec.schedule. |
| Cloudflare Workers | 5 | Yes | Cron Triggers use standard 5-field syntax. |
| GitHub Actions | 5 | Yes | Minimum interval is 5 minutes — */5 is the floor. Runs in UTC by default. Scheduled runs can be delayed under load, so never rely on exact timing. |
| systemd timers | n/a | No | Uses OnCalendar= calendar events, not cron. Every 5 minutes is OnCalendar=*:0/5. |
| AWS EventBridge | 6 | No | Six fields including Year, and you cannot use * in both Day-of-month and Day-of-week — one must be ?. |
Quartz / Spring @Scheduled | 6-7 | No | Leading seconds field, so a five-field expression shifts every value by one position. |
systemd, every 5 minutes:
[Timer]
OnCalendar=*:0/5
Persistent=true
AWS EventBridge, every 5 minutes on weekdays between 08:00 and 17:55 UTC — note the 0/5 start/step form AWS documents, the ?, and the trailing year field:
cron(0/5 8-17 ? * MON-FRI *)
EventBridge also offers rate expressions, which sidestep cron entirely:
rate(5 minutes)
Two AWS constraints worth knowing: all scheduled events run in UTC+0, and the finest resolution is one minute — "cron expressions that lead to rates faster than 1 minute are not supported."
Quartz / Spring, where the first field is seconds:
0 */5 * * * *
The general rule when moving an expression between systems: count the fields first. If the target expects six and you give it five, it will either reject the expression or silently shift every field by one position — which is how "every 5 minutes" quietly becomes "every 5 seconds" or "at 05:00."
Verify Before You Deploy
A cron expression is a piece of production configuration that nothing typechecks. Confirm it before it ships.
1. Translate it. Paste the expression into the Cron Expression Builder and read the plain-English description and the next run times back. If the description does not match your intent, stop there.
2. Check the next few fire times, not just the first. Most */N mistakes are invisible in the first two runs and only appear at the rollover — an every-7-minutes schedule looks perfect until :56.
3. Test with a faster schedule, then switch. Run * * * * * while you are validating the script, and change to */5 * * * * once the job itself is proven:
* * * * * # test version — validates the command, not the schedule
*/5 * * * * # production version
4. Confirm cron actually accepted it. crontab -l shows what is installed. On Debian and Ubuntu, grep CRON /var/log/syslog shows what actually fired; on RHEL-family systems use journalctl -u crond. A job that never appears in the log is not a schedule problem — it never ran at all.
5. Check the timezone. Cron uses the server's local time unless the crontab sets CRON_TZ. GitHub Actions and AWS EventBridge both default to UTC. A schedule that is correct in one timezone and deployed into another is one of the most common "it ran at the wrong time" causes, and daylight saving transitions make it worse.
6. Keep a manual trigger. Being able to run the task on demand — rather than waiting for the next mark — makes every subsequent debugging session faster:
// HTTP endpoint for manual execution
app.post('/api/trigger-task', async (req, res) => {
await task();
res.json({ status: 'executed' });
});
Getting the Syntax Right
Creating the perfect interval expression is crucial. A small typo can mean the difference between every 5 minutes and every 5 hours. Use our Cron Expression Builder to visually create and validate your expression. You'll see exactly when your task will run, with plain English translations that eliminate ambiguity.
Whether you need standard */5 * * * * or a complex variation restricted to specific hours and days, getting the cron syntax right ensures your automation runs exactly when you need it—no more, no less.