tooloora

Cron daily at midnight — 0 0 * * * explained

Midnight is 0:00, the first minute of the new day — which is exactly where date-based reports go wrong.

Runs locally — nothing is uploaded

Examples

In plain words

At 00:00, every day.

Field by field

  • 0Minute

    at minute 0

    Matches: 0

  • 0Hour

    between 00:00 and 00:59

    Matches: 0

  • *Day of month

    on every day of the month

    Matches: 1, 2, 3, 4, 5, 6 … 31

  • *Month

    in every month

    Matches: Jan, Feb, Mar, Apr, May, Jun … Dec

  • *Day of week

    on every weekday

    Matches: Sun, Mon, Tue, Wed, Thu, Fri, Sat

Next runs

Calculating the next runs …

These times apply in the time zone configured on the server. Cron converts nothing; this preview uses your device clock.

The expression stays in your browser — it is never sent to a server.

Other expressions

0 0 * * * runs the job once a day at midnight, meaning 0:00 — the first minute of the new day, not the last minute of the old one. Minute and hour are pinned to zero while day, month and weekday stay open. The shortcuts @daily and @midnight expand to this same line.

Which day does the midnight run belong to?

To the new one. A job that starts at 0:00 on Tuesday sees Tuesday's date, so a report meant to summarise Monday has to subtract a day explicitly, for example with date -d yesterday. This single off-by-one produces more empty reports than any other cron mistake, because the script runs fine and simply looks at a day that has barely begun.

Is midnight a good time for backups?

It is the busiest slot on most machines: log rotation, database dumps, cleanup jobs and half the crontabs on the planet start at 0:00. If your backup competes with all of them, it takes longer and is more likely to hit a locked file. Moving to 2:30 or 3:15 usually gives a quieter window with no downside.

What if the machine is switched off at midnight?

Plain cron simply skips the run; it has no memory of missed jobs. Laptops and office machines therefore need anacron, which tracks the last successful run and catches up after boot, or a systemd timer with Persistent=true. On an always-on server this does not matter, which is why the topic surprises people when they move a job to a desktop.

How to add the expression

  1. Decide whether the job needs yesterday's data; if so, compute the date inside the script instead of relying on the run day.
  2. Add 0 0 * * * plus the command to the crontab, or write @daily if the file is only read by humans.
  3. Consider moving to a quieter minute such as 0 2 * * * when the job is heavy.
  4. Log the start and end time so you can see later whether a run was skipped.

Frequently asked questions

Is 0 0 * * * the same as @daily?

Yes, and also the same as @midnight; all three describe one run per day at 0:00. The written-out version is easier to adjust later, for instance when you decide that 3 a.m. would be quieter.

Does the job run on the 1st of the month as well?

Yes. Day, month and weekday are all stars, so no calendar day is excluded. Restricting the day field is what turns a daily job into a monthly one, for example 0 0 1 * * for the first of each month.

How do I make sure only one instance runs?

Wrap the command in flock -n on a lock file. For a daily job that normally takes minutes this is cheap insurance against the one night when the database is slow and the run overtakes itself.

Where does the calculation of the next runs happen?

Entirely in your browser. The page contains the whole parser, so your expression stays on your device; there is no request to a server, no logging and no tracking of what you type.