Quick answer
The cron expression for every year is 0 0 1 1 *, which is what @yearly and @annually expand to. The fields set minute 0, hour 0, day 1 and month 1, so the job runs once a year at 00:00 on January 1. To run yearly on another date, change the day and month fields, such as 0 9 15 3 * for 9 AM on March 15.
- 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 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.
Whose New Year does 0 0 1 1 * run at?
The scheduler's. On a UTC server, the yearly run fires at midnight UTC on January 1, which is already the morning of January 1 in Tokyo (09:00) and still New Year's Eve in New York (19:00 on December 31).
For jobs that roll over a year, such as resetting annual quotas, archiving last year's records or generating year-end summaries, decide which time zone defines "the year" for your business and schedule in it: the timezone: field in GitHub Actions, the timeZone field on a Kubernetes CronJob, a time zone on EventBridge Scheduler, or the converted UTC time. January is winter in the northern hemisphere, so midnight in New York on January 1 is 0 5 1 1 * in UTC.
How do I test a cron job that runs once a year?
Carefully, because the feedback loop is a year long. A few habits help:
- Keep the logic in a script you can run by hand, and test that directly before trusting the schedule.
- Temporarily point the schedule at a minute in the near future to prove cron can start the job with its minimal environment, then restore
0 0 1 1 *. - Log a clear line on every run and alert on failure; a silent failure won't be noticed until next January.
- Check the next run date in the preview after every edit. A typo like
0 0 1 13 *is rejected, but0 0 1 * *is valid and quietly runs every month.
What does each field in 0 0 1 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 | 1–12, JAN–DEC | only in January |
| Day of week | * | 0–7 (0 and 7 are Sunday), SUN–SAT | every day of the week |
Read together: Once a year, at 00:00 on January 1.
How do I adjust 0 0 1 1 * to my own schedule?
Small edits to 0 0 1 1 * cover most real-world needs:
| Expression | What it does |
|---|---|
0 9 15 3 * | Once a year, on March 15 at 9 AM |
0 5 1 1 * | Midnight on January 1 in New York, written in UTC |
0 0 1 7 * | The start of a fiscal year that begins on July 1 |
0 0 31 12 * | 00:00 on December 31, the start of New Year's Eve |
59 23 31 12 * | The last minute of the year |
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 quarter · Every month · Last day of the month · Every day at midnight
Browse all schedules in the cron schedule library.
How do I run 0 0 1 1 * in crontab, GitHub Actions, Kubernetes and the cloud?
| Platform | Write it as | Time zone |
|---|---|---|
| Linux crontab | 0 0 1 1 * | Server's time zone; CRON_TZ= in cronie |
| GitHub Actions | - cron: "0 0 1 1 *" | UTC by default; add timezone: (IANA name) |
| Kubernetes CronJob | schedule: "0 0 1 1 *" | Set timeZone: (1.27+) |
| Cloudflare Workers | 0 0 1 1 * | UTC only; numbers weekdays from 1 = Sunday, so use names |
| Vercel Cron Jobs | 0 0 1 1 * | UTC only; Hobby runs it within the hour (±59 min) |
| AWS EventBridge Scheduler | cron(0 0 1 1 ? *) | Takes a time zone; legacy scheduled rules are UTC only |
| systemd timer | OnCalendar=*-01-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 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 1 *?
- Dropping the month (
0 0 1 * *) turns a yearly job into a monthly one, and cron won't warn you. Check that the fourth field is1. - Plain cron runs nothing if the server is down at the scheduled minute, and the next attempt is a year away. Use anacron, a systemd timer with
Persistent=true, or a monitor that expects the run. - At 00:00 on January 1, yearly, monthly and daily jobs all start at the same moment. Move the job a few minutes past midnight if they share resources.
- Yearly jobs depend on credentials, APIs and scripts that change during the year. Run the script by hand a week ahead to catch expired tokens before the scheduled run.
- 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.