Skip to main content
Tools Harbor

Cron expression for every Monday

The cron expression 0 9 * * 1 runs a job every Monday at 9 AM in Unix cron. The trailing 1 is the day-of-week — Monday in Vixie cron’s 0-based numbering (where Sunday is 0). Monday weekly schedules are typical for sprint kickoffs, weekly digests, and “start of the work week” reports. AWS EventBridge uses cron(0 9 ? * MON *) or cron(0 9 ? * 2 *) because AWS numbers days 1–7 starting Sunday, so Monday is 2.

Quick reference

PlatformExpressionMonday number
Unix / Linux crontab0 9 * * 1 or 0 9 * * MON1
Kubernetes CronJob0 9 * * 11
GitHub Actions0 9 * * 11
AWS EventBridgecron(0 9 ? * MON *) or cron(0 9 ? * 2 *)2 (named: MON)
Quartz (Java)0 0 9 ? * MON2 (named: MON)

Why is Monday 1 in Unix but 2 in AWS?

Same reason Sunday is 0 in Unix and 1 in AWS — the two numbering schemes start their week on Sunday but use different starting indexes. Vixie cron is 0-based (Sun=0, Mon=1, …, Sat=6). AWS EventBridge inherits Java Calendar’s 1-based numbering (Sun=1, Mon=2, …, Sat=7).

So when porting Monday schedules between Unix and AWS:

Unix:  0 9 * * 1        # Monday 9 AM
AWS:   cron(0 9 ? * 2 *) # same Monday 9 AM, different number
AWS:   cron(0 9 ? * MON *) # same — recommended portable form

Use the named day (MON) whenever possible — it’s unambiguous on every cron flavor that supports cron syntax (Linux, Kubernetes, AWS, Quartz, GitHub Actions, anacron). Numeric day-of-week is the most common bug source when migrating between schedulers.

Variations

ScheduleExpression
Every Monday at 9 AM0 9 * * 1
Every Monday at midnight0 0 * * 1
Every Monday at 8:30 AM (start-of-day reports)30 8 * * 1
Every Monday and Thursday at 9 AM0 9 * * 1,4
Every Monday EXCEPT public holidays(cron can’t — wrap with a holiday-check command)
First Monday of every month at 9 AM (Unix trick)0 9 1-7 * 1
First Monday of every month (AWS)cron(0 9 ? * MON#1 *)
Last Monday of every month (AWS)cron(0 9 ? * MONL *)
Every Monday in AWScron(0 9 ? * MON *)

How do I use it on each platform?

Linux crontab:

0 9 * * 1 /usr/local/bin/weekly-digest

Or with the named day for readability:

0 9 * * MON /usr/local/bin/weekly-digest

Kubernetes CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: weekly-digest
spec:
  schedule: "0 9 * * 1"
  timeZone: "America/New_York"        # ensure 9 AM is local 9 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: digest
              image: my-org/weekly-digest:1.0
          restartPolicy: OnFailure

A “Monday at 9 AM” schedule is meaningful only relative to a real timezone — UTC 9 AM is 4 AM Eastern, which is not “start of the work week” anywhere. Always set timeZone (Kubernetes 1.25+) or AWS’s ScheduleExpressionTimezone for human-aligned weekday schedules.

AWS EventBridge Scheduler:

Type: AWS::Scheduler::Schedule
Properties:
  Name: weekly-digest
  ScheduleExpression: 'cron(0 9 ? * MON *)'
  ScheduleExpressionTimezone: 'America/New_York'
  FlexibleTimeWindow:
    Mode: 'OFF'
  Target:
    Arn: !GetAtt DigestFunction.Arn
    RoleArn: !GetAtt SchedulerRole.Arn

GitHub Actions:

on:
  schedule:
    - cron: '0 9 * * 1'      # 9 AM UTC every Monday

GitHub Actions only supports UTC cron — there’s no per-workflow timezone option. For Monday digests intended for a US East team, you’d write 0 13 * * 1 (13 UTC = 9 AM EDT, 8 AM EST). Pick one offset and live with the daylight-saving drift, or move the workflow to a self-hosted runner with timezone control.

Common mistakes

Confusing 1 (Monday) with 1 (January) in different fields. The fields are positional: minute, hour, dom, month, dow. 0 9 * 1 * is “9 AM every day in January”, not “9 AM every Monday”. The day-of-week field is the fifth, after month.

Using MON in old AWS docs. Older EventBridge documentation used a numeric-only convention; some IaC validators (especially CloudFormation pre-2019) would reject cron(0 9 ? * MON *). Modern AWS accepts MON. If your linter complains, fall back to 2.

Assuming @weekly means Monday. It doesn’t — @weekly is 0 0 * * 0, midnight Sunday. There’s no @monday alias. Spell out Monday schedules explicitly.

The “first Monday” trap. 0 9 1 * 1 does NOT mean “first Monday of the month” — Vixie cron treats dom and dow as a logical OR (any day-of-month=1 OR any Monday). The schedule fires on the 1st of every month AND every Monday — way more often than intended. The 1-7 * 1 trick relies on Monday-of-the-first-week always being one specific day; AWS’s MON#1 is the explicit form.

For other schedules see common cron schedules, or build a custom expression with the Cron Expression Builder.

Frequently asked questions

Is Monday `1` in both Unix and AWS, unlike Sunday?
Yes — Monday is one of the few days where the numbering aligns. Vixie cron numbers days 0–6 starting Sunday, so Monday is `1`. AWS EventBridge and Quartz use 1–7 starting Sunday, so Monday is `2` in their numeric scheme — wait, that's not aligned. Actually, the numeric values diverge for every day: Unix Monday=1 vs AWS Monday=2. The portable form is the named day `MON`, which is unambiguous on every platform that accepts cron syntax.
Why is `0 9 * * 1` better than `@weekly` for "every Monday"?
`@weekly` is an alias for `0 0 * * 0` — midnight on Sunday, NOT Monday. There is no `@monday` shortcut in standard Vixie cron. If you want a Monday weekly schedule, you must spell it out: `0 9 * * 1` or `0 9 * * MON`. The same trap exists for `@daily` (midnight UTC) and `@hourly` (top of the hour) — convenient when they match what you want, misleading when they don't.
How do I run on the first Monday of every month?
Cron alone can't directly express "first Monday" — there's no nth-occurrence operator in Vixie cron. The trick is `0 9 1-7 * 1` which fires on any day in the 1st–7th that is also a Monday — guaranteed to be exactly one such day per month. AWS EventBridge supports the cleaner `cron(0 9 ? * MON#1 *)` using the `#` operator (n-th-weekday-of-month). Quartz also supports `MON#1`.

Need a different schedule?

Build cron expressions for Unix, Kubernetes, AWS EventBridge and Quartz — with a human-readable description and the next 5 run times.

Open the Cron Expression Builder →

Related