How to run a cron job every 5 minutes

The correct expression in all four dialects, the mistake that quietly drifts, and what to do about overlapping runs.

The answer

*/5 * * * *

Standard cron — crontab, Kubernetes CronJobs, GitHub Actions. It fires at :00, :05, :10 and so on through every hour of every day, twelve times an hour, 288 times a day.

SchedulerExpression
Standard cron*/5 * * * *
node-cron0 */5 * * * *
Quartz0 0/5 * * * ?
AWS EventBridgecron(*/5 * * * ? *)

The node-cron version has a leading 0 because node-cron adds a seconds field — without it, */5 * * * * is read as five fields and means something different. The Quartz and EventBridge versions need ? in one of the two day fields.

Reading it

*/5   *     *     *     *
 │     │     │     │     │
 │     │     │     │     └── day of week: any
 │     │     │     └──────── month:       any
 │     │     └────────────── day of month: any
 │     └──────────────────── hour:        any
 └────────────────────────── minute:      every 5th, starting at 0

*/5 in the minute field expands to 0,5,10,15,20,25,30,35,40,45,50,55. Writing it out that way is equivalent and occasionally clearer in a config file someone else will read.

The mistake that drifts

The step operator restarts at the top of every hour. That is fine for 5, which divides 60 — but not for a step that does not:

*/7 * * * *

# fires at :00 :07 :14 :21 :28 :35 :42 :49 :56
# then the hour resets, so the next run is :00 — four minutes later, not seven

Every hour has one short gap. For a health check that is harmless; for anything that assumes a fixed interval between runs it is a real bug, and it only shows up as an occasional double-processing or an occasional gap in a metric. Steps that divide 60 evenly — 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 — do not have this problem.

Variations

What you wantExpression
Every 5 minutes*/5 * * * *
Every 5 minutes during working hours, weekdays only*/5 9-17 * * 1-5
Every 5 minutes, offset by 2 to avoid a busy top-of-hour2-59/5 * * * *
Every 5 minutes, but not overnight*/5 6-22 * * *
Twice an hour instead0,30 * * * *

The offset trick — 2-59/5 giving :02, :07, :12 … — is worth knowing. If a dozen jobs all run at */5, they all start at exactly :00 and contend for the same database. Staggering them by a minute or two each costs nothing and removes a thundering herd.

In practice

crontab

# m h dom mon dow  command
*/5 * * * * /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1

Redirect both streams. Otherwise cron emails the output, and on a server with no mail configured that output is silently discarded — which is how a job fails every five minutes for a month without anyone noticing.

Kubernetes

apiVersion: batch/v1
kind: CronJob
metadata:
  name: sync
spec:
  schedule: "*/5 * * * *"
  timeZone: "Europe/Berlin"        # without this, UTC
  concurrencyPolicy: Forbid        # do not start a run while one is going
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3

concurrencyPolicy: Forbid is the one to set deliberately. The default is Allow, which starts a second pod while the first is still running.

GitHub Actions

on:
  schedule:
    - cron: "*/5 * * * *"

Always UTC, and not configurable. Scheduled workflows are also best-effort — under load GitHub delays them, sometimes by tens of minutes, and the minimum documented interval is five minutes anyway.

node-cron

import cron from 'node-cron';

// Six fields: the leading 0 is the seconds field.
cron.schedule('0 */5 * * * *', () => {
  void sync();
}, { timezone: 'Europe/Berlin' });

Overlapping runs

Cron starts a run on schedule whether or not the previous one has finished. A job that usually takes 90 seconds and occasionally takes six minutes will occasionally run twice at once — and that is usually discovered through duplicated records rather than through monitoring.

The reliable fix is a lock inside the job:

# Unix: flock, which exits immediately if the lock is held
*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/local/bin/sync.sh

For anything distributed, take the lock somewhere shared — a database row with a timestamp, or a Redis key with a TTL — because two machines running the same crontab will otherwise both fire.

Check it before you ship it

Paste your expression into the cron generator and parser and read the next eight run times. It takes a few seconds and it catches the wrong-dialect and wrong-day mistakes that otherwise surface a week later.

Frequently asked questions

What is the cron expression for every 5 minutes?

*/5 * * * * in standard cron. In node-cron, 0 */5 * * * *. In Quartz, 0 0/5 * * * ?. In AWS EventBridge, cron(*/5 * * * ? *). They differ because the field counts and the day-field rules differ, not because the schedule does.

Does */5 run exactly every 5 minutes?

It fires at :00, :05, :10 and so on — aligned to the clock, not to when you deployed. The gaps are exactly 5 minutes because 5 divides 60. A step that does not divide 60 evenly leaves a short gap at the top of each hour: */7 fires at :56 and then :00, four minutes later.

What is the difference from AWS rate(5 minutes)?

rate() counts from when the rule was created, so it drifts relative to the clock and two rules created a minute apart fire a minute apart forever. cron(*/5 * * * ? *) fires on the clock. Use rate() when the interval matters and cron() when the wall-clock time matters.

Can I run a job every 5 seconds with cron?

Not in standard cron, which has no seconds field — one minute is the finest resolution. node-cron and Quartz both add seconds, so */5 * * * * * works there. But at that frequency a scheduler is usually the wrong tool: a long-running process with its own timer avoids the process start-up cost you would pay 17,280 times a day.

Why is my every-5-minutes job overlapping itself?

Because cron starts a new run on schedule regardless of whether the previous one finished. If a run occasionally takes six minutes, you get two copies. Take a lock inside the job — a file lock, a database row, or flock on Unix — rather than assuming the schedule prevents it.

Found a mistake on this page? Tell me — a page that is confidently wrong is worse than no page.