Skip to main content
Tools Harbor

Cron expression for daily at midnight

The cron expression 0 0 * * * runs a job at midnight every day — exactly when the wall clock reads 00:00. Standard 5-field syntax works in Linux crontab, Kubernetes CronJob and GitHub Actions; AWS EventBridge needs the 6-field form cron(0 0 * * ? *). The single biggest gotcha: most schedulers default to UTC, not your local timezone.

Quick reference

PlatformExpressionDefault timezone
Unix / Linux crontab0 0 * * * or @dailySystem local
Kubernetes CronJob (≥ 1.25)0 0 * * * + spec.timeZoneUTC if timeZone unset
Kubernetes CronJob (< 1.25)0 0 * * *Controller’s local (usually UTC)
GitHub Actions0 0 * * *UTC always
AWS EventBridge Rulescron(0 0 * * ? *)UTC always
AWS EventBridge Schedulercron(0 0 * * ? *) + ScheduleExpressionTimezoneUTC if timezone unset

Which timezone is “midnight”?

This is the question that catches everyone at least once. The cron expression 0 0 * * * is a wall-clock pattern: fire when the displayed minute is 0 and the displayed hour is 0. But which clock?

Each platform has a default:

  • Linux crontab uses the system’s /etc/localtime — usually whatever you set when installing the OS. Run date on the host to see what cron sees.
  • Kubernetes < 1.25 uses the kube-controller-manager pod’s local time. On managed Kubernetes (EKS, GKE, AKS) this is UTC. Self-hosted clusters can be anything.
  • Kubernetes ≥ 1.25 accepts a spec.timeZone field. If unset, falls back to controller local. Always set it explicitly.
  • GitHub Actions is hardcoded UTC. There is no override; you must convert.
  • AWS EventBridge Rules is hardcoded UTC. EventBridge Scheduler (newer service) accepts ScheduleExpressionTimezone.

If you wrote 0 0 * * * thinking “midnight in my timezone” and the scheduler is on UTC, your job runs at 4 PM the previous day for someone on the US West Coast. The fix is either to convert your time to UTC, or to set the timezone explicitly.

Variations

ScheduleExpression
Daily at 9 AM Pacific (= 17:00 UTC)0 17 * * *
Daily at 9 AM Indian Standard Time (= 03:30 UTC)30 3 * * *
Just before midnight (23:59)59 23 * * *
5 minutes after midnight5 0 * * *
Daily at midnight in AWScron(0 0 * * ? *)
Daily at midnight Pacific in EventBridge Schedulercron(0 0 * * ? *) + ScheduleExpressionTimezone: 'America/Los_Angeles'

How do I use it on each platform?

Linux crontab:

0 0 * * * /usr/local/bin/nightly-cleanup

Or with the shortcut:

@daily /usr/local/bin/nightly-cleanup

Kubernetes CronJob (1.25+ with explicit timezone):

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-cleanup
spec:
  schedule: "0 0 * * *"
  timeZone: "America/Los_Angeles"     # critical — runs at local midnight
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: cleanup
              image: my-org/cleanup:1.0
          restartPolicy: OnFailure

AWS EventBridge Scheduler (with timezone):

Type: AWS::Scheduler::Schedule
Properties:
  Name: nightly-cleanup
  ScheduleExpression: 'cron(0 0 * * ? *)'
  ScheduleExpressionTimezone: 'America/Los_Angeles'
  FlexibleTimeWindow:
    Mode: 'OFF'
  Target:
    Arn: !GetAtt CleanupFunction.Arn
    RoleArn: !GetAtt SchedulerRole.Arn

GitHub Actions (UTC only):

on:
  schedule:
    - cron: '0 0 * * *'      # this is midnight UTC, not your local midnight

Common mistakes

Assuming 0 0 * * * runs at your midnight. It runs at the scheduler’s midnight. Always set the timezone explicitly when the platform supports it; otherwise convert to UTC.

Daylight saving silently shifts the run. A timezone like America/Los_Angeles follows DST. A job set to “midnight Pacific” is at 08:00 UTC half the year and 07:00 UTC the other half. If your downstream system expects a fixed UTC time, use UTC directly instead of a timezone.

Confusing 0 0 * * * with * * * * * or other typos. 0 0 * * * is daily; * * * * * is every minute (1440 runs/day). Easy to mis-type when copying.

For other schedules see common cron schedules, or build a custom expression with the Cron Expression Builder.

Frequently asked questions

Does `0 0 * * *` run at local midnight or UTC midnight?
Depends on the platform. Linux crontab uses the system's local timezone. Kubernetes CronJob before 1.25 uses the kube-controller-manager's timezone (usually UTC); 1.25+ accepts a `spec.timeZone` field. AWS EventBridge Rules are always UTC; EventBridge Scheduler accepts `ScheduleExpressionTimezone`. GitHub Actions runs in UTC, no timezone option. Always verify before relying on local midnight.
Why did my "daily at midnight" job run at 5 PM yesterday?
Timezone mismatch. You wrote `0 0 * * *` thinking "midnight Pacific" but the scheduler interpreted it as UTC. UTC midnight = 5 PM PST = 4 PM PDT the previous day. Either set the timezone explicitly (K8s 1.25+ `timeZone`, EventBridge Scheduler `ScheduleExpressionTimezone`) or convert to UTC: 9 AM PST = 17:00 UTC, so `0 17 * * *`.
What is `@daily` and is it the same as `@midnight`?
In Vixie cron, `@daily` and `@midnight` are aliases — both expand to `0 0 * * *`. They work in Linux crontab, anacron, Kubernetes CronJob, GitHub Actions. Neither works in AWS EventBridge. Stick with the explicit form when targeting AWS.

Need a different schedule?

Build cron expressions for Unix, Kubernetes and AWS — with a human-readable description and the next 5 run times.

Open the Cron Expression Builder →

Related