Skip to content

Every month cron: what @monthly means and why the 31st is a trap

Runs in your browser — nothing you paste leaves this page. How we prove that

Cron Expression Tester playground

Example

Five fields — minute, hour, day of month, month, day of week. Macros like @daily are supported too.

Results update as you type — press Enter to run now.

Timezone
fig. 04 — cron-expression-tester · scheduling Valid
Result

At 00:00, on the 1st of the month.

Minute0
Hour0
Day of month1
Month*
Day of week*
Next runs
  • Computed in your browser, in your timezone.

`@monthly` is shorthand for `0 0 1 * *`, a run at midnight on the first day of every month. The engine expands it and reads it as: At 00:00, on the 1st of the month.

What @monthly expands to

The macro is defined as 0 0 1 * *: minute 0, hour 0, day of month 1, every month, any day of week. Twelve runs a year, each at the very start of the month in the cron daemon's timezone.

This page's parser expands the macro and reads it as: At 00:00, on the 1st of the month. The next run times in the panel above are computed in your browser, in your own timezone, so they are not printed in this text.

Why the 1st, and the 31st trap

Day 1 exists in every month, which is why the macro uses it. A hand-written 0 0 31 * * only fires in the seven months that have a 31st, and 0 0 30 * * never runs in February. Cron does not roll over to the last day of a shorter month.

Standard cron has no last-day-of-month syntax (L belongs to Quartz, and this page's parser does not accept it). The usual workaround runs on days 28–31 and lets the command check whether tomorrow is the 1st: 0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/billing/bin/close-month with GNU date. Often the simpler fix is to run on the 1st and process the previous month, which is exactly what @monthly gives you.

Example and operational pitfalls

@monthly /opt/billing/bin/invoice-run --period previous-month is the classic use: invoicing, monthly reports, archive rotation. Because the run is rare, a failure is costly; take a lock (flock -n), make the job idempotent so it can be re-run by hand for the same period, and alert on its exit status.

Midnight on the 1st is midnight in the server's zone. On a UTC host, a team in the Americas sees the job run on the evening of the last day of the previous month, local time, which matters when the job decides which month it is closing. Name the period explicitly instead of deriving it from the current local date.

systemd, Kubernetes and GitHub Actions

systemd's OnCalendar=monthly matches cron here: *-*-01 00:00:00. Kubernetes CronJobs accept @monthly or 0 0 1 * *. GitHub Actions does not support the macro, so a workflow uses cron: '0 0 1 * *', evaluated in UTC. Related macros: @weekly below and @yearly above.

FAQ

Questions, answered.

Tap a question to expand the answer.

Standard cron cannot express it directly. Schedule 0 0 28-31 * * and have the command exit unless the next day is the 1st, or run on the 1st and process the month that just ended.

Yes. In cron implementations that support macros, @monthly is defined as 0 0 1 * *. GitHub Actions only accepts the five-field form.

More free, private DevOps tools.

The Cron Expression Tester is one tool in OpsCanopy — a growing canopy of browser-based validators, converters and testers that never touch a server.

New to Linux?  Read the Linux guide →

42 free tools, every one offline-capable — opscanopy.com works with no signup and nothing uploaded.

Scheduling on Linux? Pair this with the Cron to systemd Converter, or browse the full tools directory.

Not affiliated with or endorsed by any cron implementation. Format names are used only to describe what this tool reads. OpsCanopy is free and open.