How Cron Handles Timezones — A Practical Guide
"It worked fine in staging" is a phrase that shows up a lot in timezone bugs, because a cron expression has no idea what timezone it's supposed to mean — it just gets interpreted against whatever clock the scheduler is using. For a team spread across India and servers hosted in US or EU regions, this is one of the most common sources of "why did the job run at 3:30 AM" surprises.
The core rule: cron has no built-in timezone awareness
A cron expression like 0 9 * * * just means "when the hour field equals 9 and the minute field equals 0" — according to whatever clock the cron daemon reads. On a Linux server, that's the system's configured local timezone by default. Check it with:
$ timedatectl
Local time: Fri 2026-07-11 14:32:07 IST
Universal time: Fri 2026-07-11 09:02:07 UTC
Time zone: Asia/Kolkata (IST, +0530)
# If a job says "0 9 * * *" on this server, it fires at
# 9:00 AM IST, which is 3:30 AM UTC.Setting a specific timezone for cron jobs
Most modern cron implementations (Vixie cron, cronie, and the cron used in Debian/ Ubuntu/RHEL) support a CRON_TZ variable that overrides the timezone for entries below it, without changing the whole server's timezone:
# This entry always fires at 9:00 AM IST, regardless of server timezone CRON_TZ=Asia/Kolkata 0 9 * * * /opt/scripts/daily-report.sh # A later entry in the same crontab, unaffected, runs in UTC CRON_TZ=UTC 0 0 * * * /opt/scripts/utc-midnight-job.sh
This is safer than relying on the server's system timezone, because it survives a server migration, a container rebuild, or an ops team changing the default OS timezone for unrelated reasons.
The classic failure: server timezone changes silently
- A server gets rebuilt from a base image that defaults to UTC instead of the previous IST configuration — every "9 AM" job now fires at 2:30 PM.
- An app migrates from a self-managed VM to a managed container platform, whose containers default to UTC regardless of the previous host timezone.
- Daylight saving time shifts a job's real-world time by an hour on regions that observe DST (India does not observe DST, but many US/EU teams working with Indian teams do, causing coordination confusion twice a year).
The fix is always the same: don't depend on "whatever the server happens to be set to." Pin the timezone explicitly with CRON_TZ, or better, run everything in UTC and do timezone conversion in application code where it's testable.
How cloud schedulers handle timezone
- GitHub Actions — scheduled workflows always run in UTC, with no timezone option in the YAML. A 9:00 AM IST report needs
cron: '30 3 * * *'(3:30 AM UTC). - AWS EventBridge Scheduler — lets you set an explicit timezone per schedule (unlike the older EventBridge Rules cron, which is UTC-only), so you can specify
Asia/Kolkatadirectly and it handles DST-aware regions correctly. - AWS EventBridge Rules (classic) — cron and rate expressions are always evaluated in UTC; you must convert manually.
- GCP Cloud Scheduler — accepts an explicit timezone parameter per job, similar to EventBridge Scheduler.
- Kubernetes CronJob — historically used the controller's local timezone; recent Kubernetes versions support an explicit
timeZonefield on the CronJob spec.
Practical recommendations
- Prefer running servers in UTC and setting
CRON_TZonly for jobs that must align with a business-facing local time (e.g. "send report at 9 AM IST to the Mumbai team"). - Document the intended timezone directly as a comment next to every crontab entry — future you (and teammates) will thank you.
- For cloud schedulers without timezone support (GitHub Actions, classic EventBridge), always write the UTC-converted time and add a comment showing the IST equivalent.
- Test timezone-sensitive jobs around DST transition dates if any part of your team or infrastructure observes DST — even though India doesn't, US/EU-hosted infrastructure often does.
Frequently Asked Questions
Standard Linux cron uses the system's local timezone by default, not UTC, unless the CRON_TZ environment variable is set or the server itself is configured to run in UTC. This differs from many cloud schedulers, which default to UTC.
GitHub Actions scheduled workflows always run in UTC. There is no way to set a different timezone in the workflow YAML, so you must calculate the UTC-equivalent time yourself, for example 9:00 AM IST becomes 3:30 AM UTC.
Yes, on systems using Vixie cron or cronie you can set CRON_TZ=Asia/Kolkata on its own line directly above a crontab entry, and that line will be evaluated in the specified timezone regardless of the system default.