Skip to content

Cron schedule

Cron every weekday

The cron expression for every weekday is 0 0 * * 1-5: 00:00 on Monday, Tuesday, Wednesday, Thursday and Friday. MON-FRI works in place of 1-5 in cronie, GitHub Actions and Kubernetes.

Every weekday
Expression0 0 * * 1-5
Runs261 times in 2027
systemd OnCalendarMon..Fri *-*-* 00:00:00

How it works

The day-of-week field numbers the days 0 (Sunday) to 6 (Saturday), so 1-5 is Monday through Friday. The day-of-month field stays *, which means only the weekday decides.

Midnight at the start of a weekday is a slightly odd time for most jobs. The Monday run happens as Sunday turns into Monday, and there’s no run as Friday turns into Saturday. If the job summarizes a business day, schedule it after the day ends: 0 0 * * 2-6 runs at midnight after each of Monday to Friday.

For a working-hours version, change the hour: 0 9 * * 1-5 is covered on its own page. cron has no holiday calendar, so public holidays that fall on weekdays still get a run.

Field-by-field breakdown
FieldValueMeaning
Minute0minute 0 (on the hour)
Hour0hour 0 (12 AM)
Day of month*every day of the month
Month*every month
Day of week1-5Monday to Friday

Next runs

Scheduler clock

As on GitHub Actions without timezone, Vercel, Cloudflare and Kubernetes with timeZone "Etc/UTC".

Next run times, computed in your browser from the current time
RunYour timeUTC
1Calculating… 
2  
3  
4  
5  

Ready-to-paste versions

The same schedule for each scheduler, with what differs on each one.

crontab (Linux, macOS, BSD)

crontab -e
# m h dom mon dow  command
0 0 * * 1-5 /usr/local/bin/job.sh >> /var/log/job.log 2>&1
  • cron uses the server’s time zone. On cronie (Fedora, RHEL), a CRON_TZ=Europe/London line above the entry changes the zone for the entries below it.

GitHub Actions

.github/workflows/scheduled.yml
on:
  schedule:
    - cron: '0 0 * * 1-5'
      # timezone: 'America/New_York'  # optional; UTC when omitted
  workflow_dispatch: {}
  • Runs in UTC unless you set timezone, and only on the default branch. GitHub’s docs warn that scheduled runs can be delayed at busy times and that schedules in public repositories are disabled after 60 days without activity.
  • The start of every hour is GitHub’s busiest slot. If the exact minute doesn’t matter, move the 0 to something like 17 to get picked up sooner.
  • With a timezone that observes DST, a time skipped by the spring-forward change advances to the next valid time (the docs’ example: 2:30 AM becomes 3:00 AM).

Kubernetes CronJob

cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: scheduled-job
spec:
  schedule: "0 0 * * 1-5"
  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.timeZone is stable since Kubernetes 1.27; without it, the kube-controller-manager’s zone applies. concurrencyPolicy: Forbid skips a run while the previous Job is still active.

Vercel Cron Jobs

vercel.json
{
  "crons": [
    { "path": "/api/cron", "schedule": "0 0 * * 1-5" }
  ]
}
  • Vercel always uses UTC, doesn’t accept names like MON or JAN, and won’t let you set both day of month and day of week.
  • This fits the Hobby plan, but Hobby precision is per hour: the invocation can land anywhere within the scheduled hour.

Cloudflare Workers

wrangler.toml
[triggers]
crons = ["0 0 * * MON-FRI"]
  • Cron Triggers run on UTC. Changes can take up to 15 minutes to propagate across Cloudflare’s network.
  • Cloudflare numbers weekdays 1 = Sunday to 7 = Saturday, unlike standard cron, so the example uses names (MON, SUN), which are unambiguous.

node-cron (Node.js)

scheduler.js
import cron from 'node-cron';

// 6 fields: the first one (seconds) is optional
cron.schedule('0 0 0 * * 1-5', 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. noOverlap skips a tick while the previous run is still going.

systemd timer

job.timer + job.service
# /etc/systemd/system/job.timer
[Unit]
Description=Run job.service at midnight Monday to Friday

[Timer]
OnCalendar=Mon..Fri *-*-* 00:00:00
Persistent=true

[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 'Mon..Fri *-*-* 00:00:00', which prints the normalized form and the next elapse. AccuracySec defaults to 1min, so the start can drift by up to a minute.
  • Persistent=true runs the job at boot if the machine was off at the scheduled time, which plain cron doesn’t do.

Spring @Scheduled and Quartz

Java
@Scheduled(cron = "0 0 0 * * 1-5", zone = "UTC")
public void runJob() {
    // ...
}

// Quartz CronTrigger:
CronScheduleBuilder.cronSchedule("0 0 0 ? * MON-FRI")
  • 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 ?, and weekdays run 1 = Sunday to 7 = Saturday, which is why the example uses names.

Pitfalls

Midnight after vs before the business day

0 0 * * 1-5 fires at the start of Monday through Friday. A nightly job that processes the day’s data wants the end of each business day, which is 0 0 * * 2-6 (Tuesday to Saturday at 00:00) or 59 23 * * 1-5.

Names aren’t universal

cronie, GitHub Actions and Kubernetes accept MON-FRI. Vercel rejects names. On Cloudflare 1-5 means Sunday to Thursday, so use MON-FRI there.

Public holidays still run

cron knows weekdays, not holidays. If the job shouldn’t run on a national holiday, check a holiday list at the start of the script and exit.

Frequently asked questions

What is the cron expression for weekdays only?
Put 1-5 in the day-of-week field. 0 0 * * 1-5 runs at midnight Monday to Friday; change the first two fields for a different time.
Is 1-5 Monday to Friday in cron?
Yes, in standard cron 0 is Sunday and 6 is Saturday, so 1-5 is Monday to Friday. Quartz and Cloudflare number from 1 = Sunday, where Monday to Friday is 2-6.
Can I write MON-FRI instead of 1-5?
In cronie, Debian cron, GitHub Actions, Kubernetes, node-cron and Spring, yes. Vercel doesn’t support names.
How do I skip holidays in a cron job?
cron can’t. Check the date against a holiday list inside the job and exit early.

Last reviewed by Arielton Oberek.