Quick answer
Standard cron can't say "first Monday" directly, so the usual expression is 0 0 1-7 * * with a check in the command that only continues when the day is Monday. The first Monday always falls on days 1 to 7, and the check picks it. Writing 0 0 1-7 * 1 is a trap: cron ORs the two day fields, so it runs on days 1 to 7 and on every Monday. Cloudflare Workers and AWS EventBridge write it as 2#1.
- Live next runs
- Any time zone
- Field-by-field breakdown
- Seven platform snippets
- Daylight saving aware
How to use this schedule
- 1Copy
0 0 1-7 * *with the Copy expression button. - 2Check Next runs in your time zone to confirm it fires when you expect, including around daylight saving changes.
- 3Pick your platform under Use it on your platform and copy the ready-made crontab line, workflow, manifest or timer.
- 4Need a different time or day? Click Customize to open it in the cron expression generator and change any field.
Why doesn't 0 0 1-7 * 1 run on the first Monday?
It looks like it should: days 1 to 7, but only on Mondays. Cron reads it differently. When both the day-of-month and day-of-week fields are restricted, cron runs when either one matches. So 0 0 1-7 * 1 runs on the 1st through the 7th of every month and on every Monday, as many as eleven runs a month instead of one.
The reliable pattern is to let cron pick the candidate days and the command pick the day:
# First Monday of the month at 00:00 (in crontab, % must be escaped as \%)
0 0 1-7 * * [ "$(TZ=Europe/London date +\%u)" = 1 ] && /path/to/jobdate +%u prints the ISO weekday, 1 for Monday through 7 for Sunday, so the command only continues on the one Monday that falls in the first seven days. Set TZ to the zone your calendar lives in: CRON_TZ decides when cron starts the job, but the command still sees the server's zone. Change the number to target another weekday, such as 2 for the first Tuesday or 5 for the first Friday.
Which schedulers can say first Monday natively?
Several schedulers extend cron with # (the nth weekday of the month), so no check is needed. Watch the weekday numbers, because they differ:
| Scheduler | First Monday of the month at 00:00 |
|---|---|
| Cloudflare Workers (Sunday is 1) | 0 0 * * 2#1 |
| AWS EventBridge (Sunday is 1) | cron(0 0 ? * 2#1 *) |
| Quartz (seconds first, Sunday is 1) | 0 0 0 ? * 2#1 |
| systemd timer | OnCalendar=Mon *-*-01..07 00:00:00 |
| Linux crontab, Kubernetes, GitHub Actions, Vercel | Not supported: use 1-7 plus a weekday check |
systemd is the odd one out in a useful way: it ANDs the weekday with the date, so Mon *-*-01..07 means exactly "a Monday in the first week". The same idea covers other ordinals. The second Tuesday is days 8–14 plus a Tuesday check (Tue *-*-08..14 in systemd, 3#2 on Cloudflare), and the last Friday of the month is 6L on Cloudflare and in Quartz. For the plain start of the month, 0 0 1 * * is all you need: see every month.
What does each field in 0 0 1-7 * * mean?
| Field | Value | Allowed | Meaning |
|---|---|---|---|
| Minute | 0 | 0–59 | at minute 0 (on the hour) |
| Hour | 0 | 0–23 | at hour 0 (midnight) |
| Day of month | 1-7 | 1–31 | days 1 through 7 of the month |
| Month | * | 1–12, JAN–DEC | every month |
| Day of week | * | 0–7 (0 and 7 are Sunday), SUN–SAT | every day of the week |
Read together: At 00:00 on the first Monday of each month, using days 1–7 plus a weekday check.
The day-of-month range 1-7 narrows the candidates to the first week; a weekday check in the command (or # on platforms that support it) picks the Monday.
How do I adjust 0 0 1-7 * * to my own schedule?
Small edits to 0 0 1-7 * * cover most real-world needs:
| Expression | What it does |
|---|---|
0 9 1-7 * * | Days 1–7 at 9 AM; add the Monday check for 9 AM on the first Monday |
0 0 8-14 * * | Days 8–14, the candidates for the second Monday (with a weekday check) |
0 0 1-7 * 1 | The trap: days 1 to 7 plus every Monday of the month |
0 0 * * 2#1 | First Monday on Cloudflare Workers (not valid in crontab) |
0 0 1-3 * * | Candidate days for the first working day of the month |
Need something else? Click Customize at the top of the page to open this schedule in the cron expression generator and change any field.
Related schedules: Every Monday · Every month · Last day of the month · Every week
Browse all schedules in the cron schedule library.
How do I run 0 0 1-7 * * in crontab, GitHub Actions, Kubernetes and the cloud?
| Platform | Write it as | Time zone |
|---|---|---|
| Linux crontab | 0 0 1-7 * * + date check | Server's time zone; CRON_TZ= in cronie |
| GitHub Actions | - cron: "0 0 1-7 * *" + date check | UTC by default; add timezone: (IANA name) |
| Kubernetes CronJob | schedule: "0 0 1-7 * *" + date check | Set timeZone: (1.27+) |
| Cloudflare Workers | 0 0 * * 2#1 | UTC only; supports L and # (Monday is 2) |
| Vercel Cron Jobs | 0 0 1-7 * * + date check | UTC only; more than once a day needs Pro (Hobby: once a day) |
| AWS EventBridge Scheduler | cron(0 0 ? * 2#1 *) | Takes a time zone; legacy scheduled rules are UTC only |
| systemd timer | OnCalendar=Mon *-*-01..07 00:00:00 | Add a zone after the time, e.g. Europe/London |
In a crontab (crontab -e), the full line looks like this:
# Set TZ to the zone your calendar runs in; CRON_TZ doesn't reach the command.
0 0 1-7 * * [ "$(TZ=UTC date +\%u)" = 1 ] && /path/to/job.shThe live panel at the top of the page builds complete, copy-ready files for each platform (workflow, CronJob manifest, wrangler.toml, vercel.json, AWS CLI and systemd units) and can shift the schedule to UTC for platforms that need it.
What can go wrong with 0 0 1-7 * *?
0 0 1-7 * 1is not the first Monday: cron combines restricted day fields with OR, so it runs on days 1–7 and on every Monday.- In crontab, an unescaped
%turns the rest of the line into standard input for the command. Write\%insidedateformats or the check breaks. - The weekday check uses the server's time zone unless you set
TZfor thedatecommand. At 00:00 UTC on a Monday it is still Sunday in the Americas. - Weekday numbers differ by platform:
date +%uand cron use 1 for Monday, while Cloudflare, AWS EventBridge and Quartz use 2. Day names avoid the guesswork where they are accepted. - If either day field starts with
*(even*/2), Vixie cron and cronie switch to AND, so0 0 */2 * 1means odd days that are also Mondays. Other schedulers may still use OR. - On UTC-only platforms (Cloudflare Workers, Vercel, legacy EventBridge rules), the local time this runs at moves by an hour when daylight saving time starts or ends. Use GitHub's
timezone:, KubernetestimeZone, EventBridge Scheduler or cronie'sCRON_TZto pin it to local time.
Frequently asked questions
Last updated by the Playxoft engineering team.