Quick answer
The cron expression for every month is 0 0 1 * *, which is also what @monthly expands to. The 1 in the day-of-month field picks the first day, and 0 0 sets the time to 00:00, so the job runs twelve times a year. You can pick any day from 1 to 28 and any time; days 29 to 31 are skipped in shorter months.
- 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 * *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.
Which day of the month should a monthly job use?
Any day from 1 to 28 runs every month without exception. Days 29, 30 and 31 don't exist in every month, and cron doesn't move a run to the nearest valid date; it simply skips that month:
| Day of month | Months it runs in |
|---|---|
1 to 28 | All 12 |
29 | 11, plus February in leap years |
30 | 11 (never February) |
31 | 7 (January, March, May, July, August, October and December) |
So 0 0 31 * * is not "monthly at month end"; it is a job that silently disappears in five months of the year. For the end of the month, see the last day of the month, which needs a small workaround in standard cron.
What time of the month is best for a monthly job?
The 1st at 00:00 is the default because the previous month is complete at that moment, which suits invoicing, monthly reports and archiving. It is also the busiest moment of the month on a typical server, since most @monthly jobs start then. If the job doesn't depend on the exact moment the month closes, move it later on the 1st (0 4 1 * *) or to another day entirely, such as the 2nd or the 15th, when a retry can't collide with other month-end processes. For billing that customers see, pick a time when someone is around to handle a failure.
What does each field in 0 0 1 * * 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 | 1–31 | on the 1st 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 1st of every month, the same as @monthly.
How do I adjust 0 0 1 * * to my own schedule?
Small edits to 0 0 1 * * cover most real-world needs:
| Expression | What it does |
|---|---|
0 4 1 * * | 04:00 on the 1st, after the midnight rush |
0 0 15 * * | Mid-month, on the 15th |
0 9 1 * * | 9 AM on the 1st, for reports people read |
0 0 1,15 * * | Twice a month, on the 1st and the 15th |
0 0 1 */2 * | Every two months, on the 1st of January, March, May and so on |
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: First Monday of the month · Last day of the month · Every quarter · Every year
Browse all schedules in the cron schedule library.
How do I run 0 0 1 * * in crontab, GitHub Actions, Kubernetes and the cloud?
| Platform | Write it as | Time zone |
|---|---|---|
| Linux crontab | 0 0 1 * * | Server's time zone; CRON_TZ= in cronie |
| GitHub Actions | - cron: "0 0 1 * *" | UTC by default; add timezone: (IANA name) |
| Kubernetes CronJob | schedule: "0 0 1 * *" | Set timeZone: (1.27+) |
| Cloudflare Workers | 0 0 1 * * | UTC only; numbers weekdays from 1 = Sunday, so use names |
| Vercel Cron Jobs | 0 0 1 * * | UTC only; Hobby runs it within the hour (±59 min) |
| AWS EventBridge Scheduler | cron(0 0 1 * ? *) | Takes a time zone; legacy scheduled rules are UTC only |
| systemd timer | OnCalendar=*-*-01 00:00:00 | Add a zone after the time, e.g. Europe/London |
In a crontab (crontab -e), the full line looks like this:
0 0 1 * * /path/to/job.sh >> /var/log/job.log 2>&1The 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 * *?
- Days 29–31 are skipped in months that don't have them. Use a day from 1 to 28 for a job that must run every month.
- If the server is down at the scheduled minute, plain cron doesn't retry until next month. Use anacron, a systemd timer with
Persistent=true, or monitoring that alerts when a run is missing. - A job at 00:00 on the 1st in UTC runs on the last evening of the previous month in the Americas. If it closes "this month", compute the month in the business's time zone.
- Adding a day of the week changes the meaning:
0 0 1 * 1runs on the 1st and on every Monday, because cron combines the two day fields with 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.