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