Cron expression for Every day at 2am
0 2 * * *
Runs once a day at 2am, every day.
Next 5 runs (your local time)
These are shown in your browser’s timezone. The job itself runs in the scheduler’s timezone — often UTC — so the real run time can differ.
What people actually schedule with 0 2 * * *
- Taking full database backups during the deepest part of the traffic trough on consumer-facing apps
- Running VACUUM FULL, OPTIMIZE TABLE, or index rebuilds that need exclusive locks with minimal user impact
- Renewing TLS certificates (certbot) on a low-traffic hour so a reload blip goes unnoticed
Use 0 2 * * * on your platform
It’s the same 5-field expression everywhere — what changes is where you put it and which timezone it runs in.
Linux / crontab
0 2 * * * /path/to/your-command
Runs in the server’s local timezone — check it with timedatectl.
Full field reference: crontab(5) man page.
GitHub Actions
on:
schedule:
- cron: "0 2 * * *"
GitHub Actions always runs scheduled jobs in UTC — there is no timezone setting, and runs can be delayed under load (official docs).
Kubernetes CronJob
spec:
schedule: "0 2 * * *"
Defaults to UTC. Set spec.timeZone (Kubernetes 1.27+)
for a specific zone — see the
CronJob docs.
Quartz / Spring @Scheduled
Quartz uses 6 fields (seconds first): 0 0 2 * * *. Watch out:
Quartz day-of-week is 1=SUN … 7=SAT (not 0–6), and day-of-month /
day-of-week use ? — double-check if your schedule touches those fields
(Quartz cron reference).
Gotchas with every day at 2am schedules
- 2am literally doesn't exist on spring-forward night. In US/EU clocks jumping forward (1:59am → 3:00am), a
0 2 * * *job is silently skipped one day a year. Anacron or a systemd timer withPersistent=truewill catch up the missed run; raw cron will not. If it's a backup, that's a missing restore point nobody notices until they need it. - This is the most crowded hour on the box. Backups, certbot, package auto-updates, and OS maintenance all default near 2am, so concurrent jobs thrash the same disk and CPU. Stagger with a random sleep —
0 2 * * * sleep $((RANDOM \% 600)); /path/backup.sh— or check what else fires at 2 withgrep -r '0 2' /etc/cron*. - On fall-back night it runs twice. The 2am hour repeats when clocks roll back, so a job using local time executes again an hour later. For backups that's a wasted run; for anything that mutates state (billing, counters) it's a double-charge bug. Run in UTC to dodge both DST edges.
Will you know if this job silently fails?
Cron jobs fail quietly — a server reboots, a path changes, or an error code is ignored — and nobody notices until the data is missing. A cron monitor (a dead-man’s-switch) alerts you when a scheduled job does not check in on time.
Monitor your cron jobs with UptimeRobot →
Disclosure: this is an affiliate link — we may earn a commission if you sign up, at no extra cost to you.
Is 0 2 * * * the right schedule?
If your backup needs the day fully settled and DST-proofing, 3am avoids the nonexistent-hour skip entirely. For lighter cleanup that doesn't need the trough, 1am beats the 2am traffic jam.
Or use the interactive cron generator & explainer, read the complete cron syntax guide, or pick another common schedule: