English

Why cron runs on the day of month or the day of week

The five-field cron trap: why 30 4 1 * 1 runs on the first day of every month and every Monday, not only on the first Monday.

The report job 30 4 1 * 1 was meant to run on the first Monday of each month. Instead it ran on August 1, again on August 3, and then the next Monday. Cron did not duplicate a queue item. The schedule always meant something else.

A conventional five-field crontab line reads like this:

minute hour day-of-month month day-of-week
30     4    1            *     1

The last two restrictions do not mean "the first day that is also a Monday." They mean "the first day or a Monday." The crontab(5) manual states the rule directly: when both day fields are restricted, a match in either field is enough.

Paste 30 4 1 * 1 into the cron generator and inspect the next five runs. Dates are more useful than a confident English summary here. The preview exposes the extra Mondays that are easy to miss in a one-line review.

The asterisk changes the rule

For 04:30 only on the first day of every month, use:

30 4 1 * *

For 04:30 every Monday, use:

30 4 * * 1

In each version one day field is unrestricted with *, so the other field controls the dates. The surprising OR behavior appears when both day fields contain a restriction.

If the first day of a month is itself a Monday, one crontab line still matches once for that minute. OR widens the set of matching dates; it does not create two separate jobs.

How to express the first Monday

Pure five-field syntax cannot require both day fields to match. A practical Unix cron setup runs every Monday and lets a small wrapper script continue only when the day of month is 7 or lower. That condition can live next to tests instead of hiding inside dense shell arithmetic in a crontab line.

A scheduler with an explicit calendar rule is another option. Check its dialect first. Quartz adds seconds and ?, GitHub Actions accepts five fields, and AWS EventBridge has its own format. A string accepted by one system does not automatically carry the same meaning into another.

Devdeck deliberately parses the traditional five fields. It accepts lists, ranges, steps, and short English month and weekday names. Quartz operators such as ?, L, W, and # are outside that contract.

Check dates before deployment

When a schedule appears in a pull request, I look at the next actual dates before trusting the prose description. For 30 4 1 * 1, the list quickly contains Mondays and first days. After changing it to 30 4 1 * *, only first days remain.

The generator calculates its preview locally in the current tab and uses the browser time zone. Production cron may run in UTC, inside a container, or under CRON_TZ, so the final check still belongs beside the service configuration. The broader privacy and tooling boundary is covered in local developer tools in the browser.

Record two or three expected run dates in the review, not just the expression. That tiny schedule test catches this bug better than a polished comment beside the line.

Questions

How does cron combine day of month and day of week?

In traditional five-field crontab syntax, two restricted day fields use OR: the job runs when either the day of month or the day of week matches.

What does 30 4 1 * 1 mean?

It runs at 04:30 on the first day of every month and every Monday. It does not mean only the first Monday of each month.

Can I check this expression in devdeck?

Yes. The cron generator parses five fields and previews the next five runs in your browser time zone. It does not validate Quartz, AWS EventBridge or a specific CI dialect.

Related tools