Cron expression for Every day at 3am
0 3 * * *
Runs once a day at 3am, 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 3 * * *
- The default "safe" slot for nightly backups — late enough that the previous day's writes have drained, past the DST danger window
- Heavy ETL or analytics rollups that read yesterday's complete data and write into a warehouse
- Cache warming and search-index rebuilds so fresh data is ready before the morning traffic ramp
Use 0 3 * * * 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 3 * * * /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 3 * * *"
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 3 * * *"
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 3 * * *. 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 3am schedules
- 3am is the safe side of every DST transition. Spring-forward lands on 2am and fall-back repeats the 1–2am hour, so a
0 3 * * *local-time job neither skips nor doubles — this is exactly why "3am backup" is folklore. Don't undo that safety by switching the box to a zone that shifts at 3. - It still races the tail of the 2am crowd. If your 2am backup or OS maintenance overran, your 3am ETL starts reading a database that's mid-VACUUM and gets degraded I/O. Chain dependent jobs explicitly (a completion sentinel file, or systemd
After=) instead of trusting an hour of gap to be enough. - Long-running 3am jobs leak into business hours in other regions. A 3-hour ETL that starts at 3am server-time is finishing at 6am — fine locally, but if your users are in a +9 zone they're already at their desks. Pick the slot relative to your users' trough, not the server's, and time the job's actual duration before assuming it fits.
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 3 * * * the right schedule?
If the job is light and doesn't need the full overnight settle, 2am works but risks the spring-forward skip. If you need fresh data ready earlier for an East-Asia morning, shift toward 1am and accept a slightly less-settled dataset.
Or use the interactive cron generator & explainer, read the complete cron syntax guide, or pick another common schedule: