Cron schedule
Cron every minute
To run a cron job every minute, use * * * * *. An asterisk in all five fields matches every minute of every hour of every day, which works out to 1,440 runs a day.
| Expression | * * * * * |
|---|---|
| Runs | 1,440 times a day |
| systemd OnCalendar | *:*:00 |
How it works
One minute is the finest resolution classic cron has. The daemon wakes up once a minute, compares the current time against every line, and starts each job that matches. With five asterisks every comparison succeeds, so the job is started at second 0 of every minute, in practice a moment later, depending on how busy the daemon is.
There is no macro for this schedule: @hourly is the most frequent one. */1 * * * * and 0-59 * * * * mean the same thing as * * * * * and nobody writes them.
Before settling on every minute, check whether the job is really polling for work. 1,440 process starts a day, each re-reading config and opening new connections, often cost more than one long-running worker or a queue consumer that reacts immediately.
| Field | Value | Meaning |
|---|---|---|
| Minute | * | every minute |
| Hour | * | every hour |
| Day of month | * | every day of the month |
| Month | * | every month |
| Day of week | * | every day of the week |
Next runs
As on GitHub Actions without timezone, Vercel, Cloudflare and Kubernetes with timeZone "Etc/UTC".
| Run | Your time | UTC |
|---|---|---|
| 1 | Calculating… | |
| 2 | ||
| 3 | ||
| 4 | ||
| 5 |
Ready-to-paste versions
The same schedule for each scheduler, with what differs on each one.
crontab (Linux, macOS, BSD)
# m h dom mon dow command
* * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
# Same, but skip a run while the previous one is still going:
* * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh- cron uses the server’s time zone. On cronie (Fedora, RHEL), a
CRON_TZ=Europe/Londonline above the entry changes the zone for the entries below it.
GitHub Actions
on:
schedule:
- cron: '*/5 * * * *' # GitHub’s minimum
workflow_dispatch: {}- GitHub Actions can’t run a schedule every minute: the docs set the shortest interval at once every 5 minutes, so
*/5 * * * *is as close as it gets, and even that can be delayed at busy times.
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: scheduled-job
spec:
schedule: "* * * * *"
timeZone: "Etc/UTC"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: job
image: busybox:1.36
command: ["/bin/sh", "-c", "date; echo running"]spec.timeZoneis stable since Kubernetes 1.27; without it, the kube-controller-manager’s zone applies.concurrencyPolicy: Forbidskips a run while the previous Job is still active.
Vercel Cron Jobs
{
"crons": [
{ "path": "/api/cron", "schedule": "* * * * *" }
]
}- Vercel always uses UTC, doesn’t accept names like
MONorJAN, and won’t let you set both day of month and day of week. - This runs more than once a day, so it needs a Pro or Enterprise plan. Per Vercel’s docs, a Hobby deployment with an expression like this fails.
Cloudflare Workers
[triggers]
crons = ["* * * * *"]- Cron Triggers run on UTC. Changes can take up to 15 minutes to propagate across Cloudflare’s network.
node-cron (Node.js)
import cron from 'node-cron';
// 6 fields: the first one (seconds) is optional
cron.schedule('0 * * * * *', async () => {
await runJob();
}, { timezone: 'UTC', noOverlap: true });- The schedule lives inside the Node process: if it’s down, nothing runs, and three replicas run the job three times.
noOverlapskips a tick while the previous run is still going.
systemd timer
# /etc/systemd/system/job.timer
[Unit]
Description=Run job.service every minute
[Timer]
OnCalendar=*:*:00
[Install]
WantedBy=timers.target
# /etc/systemd/system/job.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/job.sh
# systemctl daemon-reload && systemctl enable --now job.timer- Check it with
systemd-analyze calendar '*:*:00', which prints the normalized form and the next elapse.AccuracySecdefaults to 1min, so the start can drift by up to a minute.
Spring @Scheduled and Quartz
@Scheduled(cron = "0 * * * * *", zone = "UTC")
public void runJob() {
// ...
}
// Quartz CronTrigger:
CronScheduleBuilder.cronSchedule("0 * * * * ?")- In Spring, the first of the 6 fields is seconds; the rest follow standard cron (0 or 7 = Sunday).
- In Quartz, one of the two day fields must be
?, so the day-of-week field gets it.
Pitfalls
Overlapping runs
If a run can take longer than 60 seconds, the next one starts while it’s still going; cron doesn’t check. Prefix the command with flock -n /tmp/job.lock so a second copy exits immediately instead of piling up.
Hosted schedulers may not go this low
GitHub Actions’ minimum is 5 minutes, and Vercel’s Hobby plan allows one run per day. A real cron daemon, Kubernetes, Cloudflare Workers, Vercel Pro or an in-process scheduler will fire every minute.
Mail and log noise
cron emails any output of a job to the crontab owner (or MAILTO). At 1,440 runs a day that floods a mailbox. Redirect stdout and stderr to a log file, or to /dev/null if you truly don’t need them.
Frequently asked questions
- Is * * * * * the same as */1 * * * *?
- Yes. A step of 1 over the whole range selects every value, so both match every minute. * * * * * is the conventional form.
- Does a cron job every minute start at exactly second 0?
- It is due at second 0, but cron only promises the minute. The daemon checks once a minute and forks jobs one after another, so a start a second or two late is normal.
- Can GitHub Actions run a workflow every minute?
- No. GitHub’s documentation sets the shortest interval for scheduled workflows at once every 5 minutes, and scheduled runs can be delayed further under load.
- How do I run a job every minute only during work hours?
- Restrict the hour and weekday fields: * 9-17 * * 1-5 runs every minute from 09:00 to 17:59, Monday to Friday.
Last reviewed by Arielton Oberek.