Every Day at Midnight Cron Expression
Runs once daily at 00:00 server time.
0 0 * * * in POSIX cron
Expression
0 0 * * *Try it in the tester →Field Breakdown
How It Works
Fires at 00:00 each day. Note: midnight is interpreted in the cron daemon's timezone — set `TZ=` or `CRON_TZ=` to control it.
Example Run Times
- Mon 00:00:00
- Tue 00:00:00
- Wed 00:00:00
- Thu 00:00:00
- Fri 00:00:00
Frequently Asked Questions
Which timezone does midnight refer to?
By default the server's local timezone. On Linux, set `TZ` in the user's environment or `CRON_TZ=UTC` at the top of the crontab to make it explicit.
What about daylight-saving time?
On DST spring-forward days, a midnight job will fire once at 00:00 as usual. On fall-back days some implementations fire it twice — pin the timezone to UTC to avoid the ambiguity.
0 0 * * * in Quartz / Spring
Three zeros in a row — second 0, minute 0, hour 0 — give you one firing per day at 00:00. This is the canonical Quartz nightly-batch trigger, and Spring 5.3+ aliases it as the @daily and @midnight macros.
Quartz / Spring expression
0 0 0 * * ?Try it in the tester →At 00:00:00
Unix / POSIX equivalent: 0 0 * * *
Field Breakdown
Using It with Quartz / Spring
Nightly is where the heavyweight work lives: closing out the business day, archiving rows older than the retention window, rebuilding aggregate tables, rotating audit logs. Quartz teams typically give this trigger a persistent identity (withIdentity("nightlyClose", "batch")) in a clustered JDBC JobStore so exactly one node runs it, while Spring Boot monoliths just annotate a method with @Scheduled(cron = "0 0 0 * * ?"). Misfire configuration deserves attention here, because a missed nightly run often must execute late rather than be dropped.
Quartz-Specific Notes
- →Midnight is evaluated in the trigger's timezone; a handful of zones have historically started DST at 00:00, in which case that calendar day has no midnight and the firing is skipped.
- →The trailing ? sits in day-of-week — day-of-month carries the *, which is the usual orientation when the schedule isn't weekday-based.
- →For long nightly batches, combine @DisallowConcurrentExecution with monitoring on run duration: a batch that drifts past 24 hours will collide with its own next firing.
Frequently Asked Questions
What happens if the server is down at midnight?
That firing misfires. With the default smart policy the trigger fires once promptly when the scheduler comes back; with MISFIRE_INSTRUCTION_DO_NOTHING the run is skipped until the next midnight.
Is "@daily" the same as this expression in Spring?
In Spring 5.3 and later, yes — @daily and @midnight schedule the same midnight fire times (Spring expands them to 0 0 0 * * *; its parser doesn't demand the ? Quartz does). Plain Quartz CronTrigger does not accept the macro form.
Midnight in whose timezone?
The JVM default unless overridden — set TimeZone on the Quartz trigger or zone on @Scheduled. Containerized apps frequently default to UTC, which surprises teams expecting local midnight.
0 0 * * * in AWS EventBridge
The canonical nightly job: minute 0, hour 0, every day. On EventBridge that means 00:00 UTC sharp — which is 7 or 8 PM the previous evening for US Eastern users, a detail that surprises nearly everyone the first time.
AWS EventBridge expression
cron(0 0 * * ? *)Try it in the tester →At 00:00 UTC
Unix / POSIX equivalent: 0 0 * * *
Field Breakdown
Using It with AWS EventBridge
This is the expression behind most nightly batch triggers on AWS: set it as the ScheduleExpression and target a Step Functions state machine for the ETL run, a Lambda that kicks off RDS snapshot exports, or an ECS RunTask that rebuilds nightly reports. Daily-at-midnight is also the conventional slot for log rotation to S3, billing rollups, and TTL-style cleanups of stale records. In CDK, Schedule.expression("cron(0 0 * * ? *)") wires it onto a Rule construct directly.
AWS-Specific Notes
- →Midnight here is midnight UTC. To run at local midnight, switch to EventBridge Scheduler and set ScheduleExpressionTimezone, e.g. America/New_York — classic rules cannot do this.
- →00:00 UTC is one of the busiest scheduled instants on all of AWS; expect a few extra seconds of jitter and consider 00:07 (cron(7 0 * * ? *)) for latency-sensitive nightlies.
- →The day-of-month field holds the * and day-of-week takes the mandatory ?; flipping them, cron(0 0 ? * * *), is also valid and means the same thing.
Frequently Asked Questions
My nightly job runs at 5pm Pacific — is the rule broken?
No. EventBridge rules evaluate in UTC, and 00:00 UTC is 16:00 or 17:00 Pacific depending on daylight saving. Use EventBridge Scheduler with a timezone if you need true local midnight.
What is the event payload my Lambda receives from this rule?
A small JSON envelope with source aws.events, detail-type Scheduled Event, an empty detail object, and a time field carrying the scheduled UTC timestamp — useful as a deterministic run date for your batch.
Will the run be skipped if AWS is under load at midnight?
Not skipped — EventBridge guarantees at-least-once invocation — but it may start up to a minute late. Build your SLA around 00:01 rather than 00:00 exactly.
0 0 * * * in GitHub Actions
One run per day at 00:00 UTC — minute and hour both pinned to zero. It is probably the most-committed cron line on all of GitHub, and that popularity is exactly its weakness: midnight UTC is the single most oversubscribed moment in the Actions scheduler.
GitHub Actions expression
0 0 * * *Try it in the tester →At 00:00 UTC
Unix / POSIX equivalent: 0 0 * * *
Field Breakdown
Using It with GitHub Actions
The canonical nightly job: on: schedule: - cron: "0 0 * * *" in workflow YAML, effective once merged to the default branch. Daily-at-midnight powers stale-issue and stale-PR sweeps, nightly builds published to a prerelease tag, scheduled CodeQL analysis, and lockfile-refresh PRs. Because everyone else does the same, GitHub's documentation specifically recommends offsetting — a schedule like "37 1 * * *" is functionally still nightly but can start tens of minutes sooner in practice.
GitHub-Specific Notes
- →00:00 UTC combines the hour boundary and the day boundary; delays here are the longest on the platform, and during heavy load the tick can be dropped without retry.
- →Midnight UTC is 4–5pm Pacific depending on DST and always 9am in Japan (no DST there) — a nightly job is only nightly in UTC terms, since the schedule event has no timezone option.
- →On public repos this schedule stops silently after 60 days without repository activity; nightly workflows are the most common victims of that rule because automation-only repos accrue no commits.
Frequently Asked Questions
My nightly workflow ran at 00:26 — is something broken?
No. Midnight UTC is the most congested slot in GitHub's scheduler, and a 20-30 minute delay there is well within normal. Shift the schedule to an off-peak minute and hour if punctuality matters.
Can I write @daily instead of 0 0 * * * in workflow YAML?
No — GitHub Actions documents only the 5-field cron syntax for the schedule event. Spell out the five fields.
How do I get the run at midnight in my local timezone?
Convert to UTC and hardcode it: midnight US Eastern is 0 4 * * * during EDT or 0 5 * * * during EST. The schedule will not follow daylight saving, so pick one offset or update the file twice a year.