HTTPfy

First Monday of every month

At 09:00 on the first Monday of each month. Standard cron cannot say 'first Monday', so the line fires on days 1-7 and a guard lets only the Monday through.

12runs in the next 365 days
28 days / 35 daysshortest / longest gap between runs
09:00next run, Mon 07 Dec UTC

What it is used for

Monthly team meetings, patch-day style maintenance and monthly reports that must land on a working day.

Field by field

FieldValueMatches
Minute00
Hour99
Day of month1-71, 2, 3, 4, 5, 6, 7
Month*every value
Day of week*every value

Because cron cannot express this directly, the crontab line carries a guard that skips the unwanted days:

0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /path/to/job

Next 10 run times

Computed when this page loaded (2026-11-11 02:24 UTC), assuming the scheduler runs in UTC. Only the days that pass the guard are listed.

#UTCYour local time
1Mon 2026-12-07 09:00
2Mon 2027-01-04 09:00
3Mon 2027-02-01 09:00
4Mon 2027-03-01 09:00
5Mon 2027-04-05 09:00
6Mon 2027-05-03 09:00
7Mon 2027-06-07 09:00
8Mon 2027-07-05 09:00
9Mon 2027-08-02 09:00
10Mon 2027-09-06 09:00

Things to watch with this schedule

  • The obvious line 0 9 1-7 * 1 is wrong: when both day-of-month and day-of-week are restricted, cron runs when EITHER matches, so it fires on days 1 to 7 and on every Monday. That is why the day-of-week test moves into the command.
  • EventBridge and Quartz support the # operator (2#1 = first Monday, because day 2 is Monday there), and systemd ANDs the weekday with the date, so the first-Monday rule is native there.

Kubernetes CronJob

Kubernetes uses the same five fields. Set spec.timeZone (stable since Kubernetes 1.27), otherwise the schedule follows the time zone of kube-controller-manager.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: first-monday-of-every-month
spec:
  schedule: "0 9 1-7 * *"
  timeZone: "Etc/UTC"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: job
              image: debian:bookworm-slim
              command: ["/bin/sh", "-c", "if [ \"$(date +%u)\" = \"1\" ]; then /app/run-job.sh; fi"]

GitHub Actions

Workflow schedules use five-field cron in UTC and run only from the default branch. Runs at minute 0 are the most likely to be delayed or dropped when GitHub is busy; an odd minute such as 7 or 23 starts more reliably.

on:
  schedule:
    - cron: '0 9 1-7 * *'   # UTC

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - name: Only continue on the intended day
        id: gate
        run: [ "$(date +%u)" = "1" ] && echo "go=1" >> "$GITHUB_OUTPUT" || true
      - run: ./run-job.sh
        if: steps.gate.outputs.go == '1'

systemd timer

The equivalent OnCalendar= value is Mon *-*-01..07 09:00:00. Check it with systemd-analyze calendar 'Mon *-*-01..07 09:00:00', which prints the next elapse time. Persistent=true runs a missed job after downtime, which classic cron never does.

[Unit]
Description=First Monday of every month

[Timer]
OnCalendar=Mon *-*-01..07 09:00:00
Persistent=true

[Install]
WantedBy=timers.target

AWS EventBridge

EventBridge rules take six fields (minute, hour, day-of-month, month, day-of-week, year) in UTC; EventBridge Scheduler can use a time zone. One of day-of-month and day-of-week must be ?, and day-of-week runs 1-7 from Sunday, so names are used here.

cron(0 9 ? * 2#1 *)

Quartz Scheduler (Java)

Quartz adds a leading seconds field and needs ? in either day field; days of the week are 1 (SUN) to 7 (SAT).

0 0 9 ? * 2#1

Spring's @Scheduled(cron = ...) also uses six fields, but numbers days of the week 0-7 with Sunday as 0 or 7, like Unix cron; the names shown above work in both.

Related schedules