Cron timezone issues: why your job runs at the wrong time and how to fix it

Cron evaluates schedules against the system's local timezone, not UTC. This causes two classes of bugs: the server is set to UTC but you wrote the schedule in your local time, or the server is in a DST-observing timezone and your job skips or duplicates when the clocks change. Both are frustrating and both are preventable.

Schedule pattern
Cron timezone pitfalls
timezone
Category
Pitfalls
Common cron mistakes and fixes

How this is calculated

On a UTC server, a 0 2 * * * job runs at 2 AM UTC. If you're in US Eastern time and thought '2 AM my time,' the job actually fires at 10 PM Eastern (or 9 PM depending on DST). The fix is to either set CRON_TZ=America/New_York in your crontab or adjust the hours: 0 7 * * * for 2 AM Eastern in standard time, 0 6 * * * during DST. For DST spring-forward, cron skips the hour that doesn't exist. For fall-back, cron runs the job twice for the repeated hour (typically at the first occurrence). If your job is idempotent this is fine. If not, add a guard.

Verdict

Always set CRON_TZ in your crontab if you care about local time. Prefer UTC for server jobs and document that decision. For DST safety, either accept the skip/double behavior (which is usually harmless for idempotent jobs) or switch to systemd timers with a monotonic calendar event.

More Cron scenarios

Frequently asked questions

What is a Cron Job?
A cron job is a scheduled task that runs automatically on a Unix-like operating system (like Linux or macOS) at specific intervals. It is heavily used by developers to run background tasks like database backups, cache clearing, or sending nightly emails.
What do the 5 parts of a cron expression mean?
From left to right, the 5 fields are: Minute (0-59), Hour (0-23), Day of the Month (1-31), Month (1-12), and Day of the Week (0-6, where 0 and 7 are Sunday).
What does the asterisk (*) mean in Cron?
The asterisk acts as a wildcard, meaning 'every'. For example, if the minute field is an asterisk, the task runs every single minute. If the month field is an asterisk, the task runs every single month.
How do I run a task every 5 minutes?
To run a task every 5 minutes, use the slash operator in the minute field like this: */5 * * * *. The */5 means 'every 5th minute'.