Cron every day at midnight
Expression: 0 0 * * * — At 00:00, every day.
At 00:00, every day
Next 5 runs in UTC, from 1 Jan 2026 (example)
When to use it
Midnight is the default slot for daily backups, cleanup scripts, invoice generation and report e-mails. Both the minute and the hour are fixed at zero, while day, month and weekday stay open, so the job runs once per calendar day. Because so many systems pick exactly this moment, shared databases, storage buckets and third-party APIs are often busiest right at 00:00 — moving a heavy job a little later usually makes it faster and more reliable. Daily jobs also give natural checkpoints: name output files after the date, keep a rolling retention window, and verify restorability of backups regularly.
Field by field
| Field | Value | Meaning |
|---|---|---|
| Minute | 0 | only 0 |
| Hour | 0 | only 00:00 |
| Day of the month | * | every day of the month |
| Month | * | every month |
| Day of the week | * | every day of the week |
Real-world example
30 2 * * * pg_dump -Fc shop | gzip > /backups/shop-$(date +\%F).sql.gz
Daily database backup at 02:30. Note the escaped percent sign: in a crontab, an unescaped % starts the command input and silently breaks the line.
Variants
| Expression | Meaning |
|---|---|
@daily | At 00:00, every day — same schedule, shorter to write |
30 2 * * * | At 02:30, every day — daily at 02:30, a quieter time for backups |
0 0 * * 1-5 | At 00:00, on weekdays (Monday to Friday) — midnight on weekdays only |
Pitfalls
- Midnight in which zone? Servers and cloud schedulers usually run in UTC, which is not midnight for your users.
- A job at 00:00 that processes "yesterday" must compute the date carefully across month and year boundaries.
- In a crontab, % must be written as \% — date +%F works in a shell but not in a cron line.
Questions
How do I run something at the end of each month instead?
Cron has no last-day keyword. Schedule 55 23 28-31 * * and let the script continue only when tomorrow is the first, e.g. [ "$(date -d tomorrow +\%d)" = 01 ] with GNU date.
Does 0 0 * * * use my local midnight?
It uses the time zone of the machine or scheduler running cron. Check with date on the server, or set CRON_TZ in cronie and timeZone in Kubernetes.
Should backups run at midnight?
Prefer a quieter, randomised minute such as 02:37. Fewer jobs compete for disks and networks, and a failure still leaves time to retry before the working day.
The same schedule elsewhere
- GitHub Actions:
on: schedule: - cron: '0 0 * * *'. Always UTC, and runs can start several minutes late when runners are busy. - Kubernetes CronJob:
schedule: "0 0 * * *"; setconcurrencyPolicy: Forbidto prevent overlapping pods andtimeZoneif you need local time. - Cloudflare Workers:
crons = ["0 0 * * *"]under[triggers]in wrangler.toml, evaluated in UTC.
Before you deploy
Use absolute paths, because cron starts commands with a minimal PATH and without your shell profile. Make the job idempotent so a late or repeated run does no harm, log its exit code, and let a dead-man's-switch monitor alert you when an expected run never happens.