New blogMaking an AI Agent You Can Trust With Money

★ Cron cheat sheet

Cron Expression: Last Day of the Month

Standard cron has no “last day” symbol. The usual pattern runs on days 28–31 and lets the job check whether tomorrow is the 1st.

  • Runs in your browser
  • No signup

The expression

0 0 28-31 * *

At 00:00 on every day-of-month from 28 through 31.

Read this first:This expression alone runs up to four times at the end of a month. Pair it with a guard in the command: [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/my-job (GNU date; escape % in crontab). AWS EventBridge supports L directly: cron(0 0 L * ? *).
  1. 0Minute
  2. 0Hour
  3. 28-31Day of month
  4. *Month
  5. *Day of week

✓ Valid 5-field cron

Quick picks

Typing by hand. Pick a pattern above to build one with inputs instead.

In plain English

At 00:00 on every day-of-month from 28 through 31.

Next 10 runs

Paste it into your platform

Time zone: UTC

0 0 28-31 * * /usr/local/bin/my-job >> /var/log/my-job.log 2>&1
  • OKRuns in the server’s local time zone. cronie (RHEL, Fedora) accepts a CRON_TZ= line above the job; Debian’s cron does not.
  • Heads-upEscape % as \% in the command: an unescaped % ends the command and starts stdin.
  • Heads-upcron runs with a minimal PATH and no login shell. Use absolute paths and redirect output, or failures disappear.
  • Linux crontab docs ↗

Cron jobs failing silently?

I build background job systems with retries, alerting and dashboards, so you hear about failures before your customers do.

Make my jobs reliable
01

What 0 0 28-31 * * means

Field by field:

Field breakdown
FieldValueMatches
Minute00
Hour00
Day of month28-3128, 29, 30, 31
Month*any
Day of week*any
02

Gotchas

  • Don’t use 31 alone: it silently skips every month with 30 days, and February.
  • Quartz-style L works on AWS EventBridge and Spring, not on Linux cron, GitHub Actions, Vercel or Kubernetes.
03

Does it run everywhere?

Checked against each platform's rules, assuming UTC:

Platform support
PlatformVerdictWhy
Linux crontabWorks, with a catchEscape % as \% in the command: an unescaped % ends the command and starts stdin.
GitHub ActionsWorks, with a catchRuns at minute 0 are the most likely to be delayed or dropped under load. GitHub suggests another minute, e.g. 17.
VercelWorks, with a catchVercel now labels Cron Jobs “legacy” in favour of Vercel Schedules, which add time zones. Existing crons keep working.
AWS EventBridgeWorksSix fields: the year is added at the end, and one of day-of-month / day-of-week must be ?.
KubernetesWorks, with a catchWithout timeZone, the schedule uses the kube-controller-manager’s local zone. TZ= or CRON_TZ= inside schedule is rejected from v1.29.
04

Questions engineers actually ask

Is there a cron expression for the last day of the month?
Not in standard 5-field cron. Run on 28-31 and check in the job whether tomorrow is the 1st, or use AWS EventBridge’s L.
Why not just run on the 1st at 00:00?
Often that is the better answer: “after month end” and “last day” usually mean the same job. Run at 00:00 on the 1st and process the previous month.
Why does my job run on days I didn't expect?
Probably the day-of-month / day-of-week OR rule. If both fields are set, cron runs when either matches. Set one of them to * to get the usual AND behaviour.
What time zone does cron use?
Whatever the scheduler uses: the server's zone for Linux cron, UTC for Vercel and EventBridge rules, UTC by default on GitHub Actions (with an optional timezone key) and the controller's zone on Kubernetes unless spec.timeZone is set.
05

Other schedules

06

More from the workbench

Fine print

Tools: on the house.
Engineer: by the hour.

Infra that doesn't page you at 3am costs a little more. I build background job systems with retries, alerting and dashboards.

I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.