Quick answer
The cron expression for every day at midnight is 0 0 * * *. The first 0 is the minute and the second 0 is the hour, so the job fires at 00:00, the first minute of each new day. The three asterisks allow every day of the month, every month and every day of the week. The shortcuts @daily and @midnight mean exactly the same thing.
- Live next runs
- Any time zone
- Field-by-field breakdown
- Seven platform snippets
- Daylight saving aware
How to use this schedule
- 1Copy
0 0 * * *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.
What does 0 0 * * * mean?
Cron reads five fields from left to right: minute, hour, day of month, month and day of week. 0 0 * * * pins the first two and leaves the rest open:
- Minute
0: on the hour, and at no other minute - Hour
0: midnight in 24-hour time (cron has no AM or PM) * * *: every day of every month, whatever the weekday
So the job fires once, at 00:00, as the date ticks over. That is the start of the day, not the end of it. A job that summarizes "today" at 00:00 is really looking at a day that has only just begun, so have it query the previous date explicitly instead of using the current time.
Most cron daemons also accept the macros @daily and @midnight. Both expand to 0 0 * * *, but not every scheduler understands them, so the five-field form is the safest one to copy between platforms.
Why is midnight such a busy time for cron jobs?
Midnight is the default answer to "when should this run once a day?", which is exactly why it is crowded. Log rotation, backups, report generation and billing jobs all pile onto 00:00, often on the same machine or against the same database, and compete for CPU, disk and API quotas in the same second.
If your job doesn't truly need to start at 00:00, move it a few minutes off: 17 0 * * * (00:17) keeps it nightly but out of the stampede. If it does need the stroke of midnight, for example to close a day's ledger, keep 0 0 and make the job idempotent so a retry or a manual rerun can't count anything twice.
Also decide whose midnight. Cron uses the server's time zone, which on most cloud machines is UTC. Midnight UTC is 7 or 8 PM the previous evening in New York and 5:30 AM in India, so a "nightly" job can land in the middle of someone's working day.
What does each field in 0 0 * * * 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–31 | every day 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: Once a day at 00:00, the very first minute of each new day.
How do I adjust 0 0 * * * to my own schedule?
Small edits to 0 0 * * * cover most real-world needs:
| Expression | What it does |
|---|---|
17 0 * * * | Every night at 00:17, just past the midnight rush |
59 23 * * * | One minute before midnight, the last minute of each day |
0 0 * * 1-5 | Midnight on weekdays only |
0 0,12 * * * | Midnight and noon, twice a day |
0 0 */2 * * | Midnight on odd days of the month (1st, 3rd, 5th …), restarting each month |
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 day at noon · Every day at 9am · Twice a day · Every week
Browse all schedules in the cron schedule library.
How do I run 0 0 * * * in crontab, GitHub Actions, Kubernetes and the cloud?
| Platform | Write it as | Time zone |
|---|---|---|
| Linux crontab | 0 0 * * * | Server's time zone; CRON_TZ= in cronie |
| GitHub Actions | - cron: "0 0 * * *" | UTC by default; add timezone: (IANA name) |
| Kubernetes CronJob | schedule: "0 0 * * *" | Set timeZone: (1.27+) |
| Cloudflare Workers | 0 0 * * * | UTC only; numbers weekdays from 1 = Sunday, so use names |
| Vercel Cron Jobs | 0 0 * * * | UTC only; Hobby runs it within the hour (±59 min) |
| AWS EventBridge Scheduler | cron(0 0 * * ? *) | Takes a time zone; legacy scheduled rules are UTC only |
| systemd timer | OnCalendar=*-*-* 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 * * * /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 * * *?
- Midnight belongs to the new day. A report built at 00:00 that asks for "today's" data gets an almost empty day, so query yesterday's date explicitly.
- Cron uses the server's clock. On a UTC server,
0 0 * * *runs at 7 or 8 PM the previous evening in New York, depending on daylight saving time. - If the machine is off or asleep at 00:00, plain cron skips that night entirely. Use anacron or a systemd timer with
Persistent=trueif a missed run must catch up. - Many nightly jobs start at exactly 00:00. Stagger yours (
7 0 * * *,23 0 * * *) when several of them hit the same database. - 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.