DeveloperDevOps

How to Read a Cron Expression

The complete cron reference — five-field syntax, operators, special strings, common patterns, timezone conversion for IST servers, and the mistakes that silently break scheduled jobs.

7 min read

TL;DR — Key Points

Cron syntaxFive fields separated by spaces: minute (0–59), hour (0–23), day of month (1–31), month (1–12), day of week (0–7, where both 0 and 7 are Sunday).
* (asterisk)Wildcard — matches every value for that field. '* * * * *' runs every minute of every hour of every day.
/ (slash)Step values. '*/5' in the minute field means 'every 5 minutes'. '0 */2 * * *' means 'every 2 hours at minute 0'.
- (hyphen)Range. '1-5' in the day-of-week field means Monday through Friday. '9-17' in the hour field means 9 AM through 5 PM.
, (comma)List. '1,15' in the day-of-month field means the 1st and 15th. '1,3,5' in day-of-week means Monday, Wednesday, Friday.
TimezoneCron runs in the system timezone of the server — typically UTC on cloud infrastructure. A cron scheduled for 9 AM runs at 9 AM UTC, not 9 AM IST.

What Is Cron?

Cron is a time-based job scheduler built into Unix and Linux operating systems. It runs as a background daemon and executes commands at specified intervals — from once a minute to once a year. The name comes from the Greek word for time, chronos. Every web server, database server, CI/CD pipeline, and cloud function scheduler you interact with either uses cron expressions directly or implements a scheduling format derived from cron.

Cron expressions appear in Linux crontabs, AWS EventBridge Scheduler, Google Cloud Scheduler, GitHub Actions workflows, Kubernetes CronJobs, Laravel task scheduling, Celery Beat, Sidekiq Scheduler, and virtually every modern application framework that needs scheduled tasks. Understanding cron expressions is a foundational skill for anyone who builds or maintains backend systems.

The cron expression format consists of five space-separated fields representing: minute, hour, day of month, month, and day of week. Reading left to right, you can describe any recurring schedule. 0 9 * * 1-5 means "at minute 0, hour 9, any day of month, any month, Monday through Friday" — which translates to every weekday at 9:00 AM.

The asterisk (*) is the wildcard meaning "every value." The expression * * * * * means every minute of every hour of every day — the most frequent valid cron schedule. Most production jobs should not run this frequently; use */5 or */15 for polling tasks and reserve minute-by-minute execution for genuinely time-critical work.

The Five Fields: Complete Reference

Each field has a defined range and set of special characters it supports:

FieldPositionValid RangeOperatorsExample
Minute1st0–59* / - ,30 = at minute 30; */15 = every 15 min
Hour2nd0–23* / - ,9 = 9 AM; 0 = midnight; 13 = 1 PM
Day of Month3rd1–31* / - , ?1 = 1st of month; 15 = 15th; L = last day
Month4th1–12* / - ,1 = January; 12 = December; 6 = June
Day of Week5th0–7 (0,7=Sun)* / - , ?1 = Monday; 5 = Friday; 1-5 = Mon–Fri

The four special operators work as follows:

* (asterisk)

Every value. '* in hours' means every hour (0–23).

/ (slash)

Step. '*/5 in minutes' means every 5 minutes: 0, 5, 10, 15...

- (hyphen)

Range. '1-5 in weekday' means Monday through Friday.

, (comma)

List. '1,15 in day-of-month' means the 1st and the 15th.

Common Cron Expressions Reference

A comprehensive reference of the most commonly used cron expressions in production systems:

ExpressionMeaningCommon Use Case
* * * * *Every minuteHealth checks, polling (use sparingly)
*/5 * * * *Every 5 minutesMetrics collection, cache warming
*/15 * * * *Every 15 minutesSync jobs, lightweight data pulls
0 * * * *Every hour at minute 0Hourly reports, cleanup tasks
0 */2 * * *Every 2 hoursSemi-regular background jobs
0 9 * * *Every day at 9:00 AMDaily email digests, morning reports
0 9 * * 1-5Weekdays at 9:00 AMBusiness day reports, work notifications
0 0 * * *Every day at midnightDaily DB backups, log rotation
0 0 * * 0Every Sunday at midnightWeekly cleanup, weekly digests
0 0 1 * *1st of every month at midnightMonthly billing, monthly reports
0 0 1 1 *January 1st at midnightYearly archiving, annual reset tasks
30 9 * * 1Every Monday at 9:30 AMWeekly standup reminders, sprint starts
0 18 * * 5Every Friday at 6:00 PMEnd-of-week reports, weekly digests
0 2 * * *Every day at 2:00 AMMaintenance windows, heavy DB jobs
0 9,17 * * *Every day at 9 AM and 5 PMTwice-daily reports, shift summaries

Special Strings (@daily, @weekly, etc.)

Many cron implementations support special string shortcuts that are easier to read than numeric expressions. These are not part of the POSIX standard but are supported by GNU crontab, Vixie cron, and most Linux distributions:

StringEquivalent ExpressionMeaning
@yearly (or @annually)0 0 1 1 *Once a year at midnight on January 1st
@monthly0 0 1 * *Once a month at midnight on the 1st
@weekly0 0 * * 0Once a week at midnight on Sunday
@daily (or @midnight)0 0 * * *Once a day at midnight
@hourly0 * * * *Once an hour at minute 0
@rebootN/ARuns once at system startup (not POSIX standard)

Note: Special strings are not universally supported — GitHub Actions cron, AWS EventBridge, and Kubernetes CronJobs require standard 5-field expressions. Always use the numeric equivalent when writing cross-platform cron schedules.

Timezone Conversion for Cron (IST and Other Zones)

This is the most common source of cron bugs for Indian developers. Cloud servers (AWS EC2, GCP Compute Engine, DigitalOcean Droplets) default to UTC. If you write a cron expression for "9 AM" assuming IST, it will run at 9 AM UTC — which is 2:30 PM IST, not 9 AM IST.

The solution is to convert your target local time to UTC before writing the expression. India Standard Time (IST) is UTC+5:30. To run a job at 9:00 AM IST, subtract 5 hours 30 minutes: 9:00 AM − 5:30 = 3:30 AM UTC. The cron expression is 30 3 * * *.

Target Local TimeUTC Cron ExpressionExplanation
9:00 AM IST30 3 * * *IST = UTC+5:30, so 9:00 AM IST = 3:30 AM UTC
6:00 PM IST30 12 * * *6:00 PM IST = 12:30 PM UTC
Midnight IST30 18 * * *Midnight IST (00:00 IST) = 6:30 PM UTC previous day
9:00 AM EST0 14 * * *EST = UTC-5, so 9:00 AM EST = 2:00 PM UTC
9:00 AM GMT0 9 * * *GMT = UTC+0, same as UTC
9:00 AM SGT0 1 * * *SGT = UTC+8, so 9:00 AM SGT = 1:00 AM UTC

Some modern schedulers (AWS EventBridge Scheduler, GCP Cloud Scheduler) allow specifying a timezone directly in the schedule configuration. When available, always use this instead of manually converting to UTC — it makes your intent clear and handles DST automatically for regions that observe it.

Common Cron Mistakes and How to Fix Them

These are the errors that cause cron jobs to silently run at the wrong time or not run at all:

Forgetting server runs in UTC

0 9 * * * → runs at 9 AM UTC, which is 2:30 PM IST, not 9 AM IST

✓ Fix: Adjust hour for server timezone. For 9 AM IST on a UTC server: 30 3 * * * (3:30 AM UTC = 9:00 AM IST)

Using both day-of-month and day-of-week

0 9 15 * 1 → some cron implementations run on the 15th OR Monday, not the 15th AND Monday

✓ Fix: Use one or the other, not both. Check your cron implementation's behaviour.

Sunday is both 0 and 7

0 9 * * 7 → should work on most systems, but 0 9 * * 8 → invalid

✓ Fix: Use 0 for Sunday consistently. Some systems accept 7 but 0 is safer and more portable.

Step values starting at wrong point

*/2 in hours field: runs at 0, 2, 4, 6... not at 1, 3, 5...

✓ Fix: Understand that */2 means every Nth value starting from the minimum (0). To start at 1: use 1-23/2.

Month numbers start at 1, not 0

0 0 1 0 * → invalid, month 0 does not exist

✓ Fix: Months are 1–12. January = 1, December = 12. Day of week is 0–7. Only months start at 1.

No space between fields

0 9** * → syntax error

✓ Fix: Five fields separated by single spaces: 0 9 * * *

How to Write the Right Expression

Apply the following approach for common scheduling requirements:

1

You want to run a job every N minutes (e.g. every 10 minutes)

Use step syntax: */10 * * * *. This runs at 0, 10, 20, 30, 40, 50 minutes past every hour. Note: it cannot start at an arbitrary minute like 3, 13, 23 — use a list for that: 3,13,23,33,43,53 * * * *.

2

You want to run a job on weekdays only during business hours

Combine day-of-week range and hour range: 0 9-17 * * 1-5. This runs every hour from 9 AM to 5 PM, Monday through Friday only.

3

You want a job to run at 9 AM India time on a UTC server

Convert IST to UTC: IST is UTC+5:30, so 9:00 AM IST = 3:30 AM UTC. Expression: 30 3 * * *. If the server observes DST for other regions, always verify the UTC equivalent.

4

You want to run a job on the last day of every month

Standard cron has no direct 'last day' syntax except in extended cron implementations (like Quartz which uses L). In standard cron: use a script that checks if tomorrow is the 1st of the next month. Or use 0 0 28-31 * * with a script that validates the actual last day.

5

You want to run a job every 2 hours starting at 1 AM, not midnight

Step values always start from the minimum (0 for hours). Use a list instead: 0 1,3,5,7,9,11,13,15,17,19,21,23 * * *. Or use an offset step: 1-23/2 in the hour field.

6

You are not sure if your cron expression is correct before deploying

Use the Cron Expression Parser tool to visualise the next 10 execution times. Never deploy a cron job without verifying the next runs. A typo in the expression can cause jobs to run every minute or never run.

Real-World Cron Schedule Examples

Common production scheduling requirements with their correct cron expressions:

RequirementExpressionNote
Send daily email digest at 8 AM IST30 2 * * *3:30 AM UTC = 8 AM IST on UTC server
Run DB backup every night at 2 AM0 2 * * *2 AM server time — confirm server timezone
Generate weekly report every Monday 9 AM0 9 * * 19 AM server time on Mondays
Clear cache every 30 minutes*/30 * * * *Runs at :00 and :30 every hour
Run billing on 1st of month at midnight0 0 1 * *Midnight server time, 1st of every month
Health check every 5 minutes on weekdays*/5 * * * 1-5Monday–Friday only
Archive logs at 11:59 PM every night59 23 * * *One minute before midnight
Sync data at 9 AM and 6 PM IST on UTC server30 3,30 12 * * *9 AM IST = 3:30 UTC, 6 PM IST = 12:30 UTC

Frequently Asked Questions

What is the difference between cron and crontab?

Cron is the daemon (background service) that reads schedules and executes commands at the specified times. Crontab (cron table) is the configuration file that stores the schedules. You edit crontab files using 'crontab -e' to schedule jobs for the current user. The system crontab at /etc/crontab has an additional field for the user to run as. Most cloud platforms (AWS EventBridge, GitHub Actions, GCP Cloud Scheduler) accept cron expressions directly in their configuration, without using the system crontab.

Does cron run in UTC or local time?

Cron runs in the timezone configured for the operating system (the TZ environment variable, or the system timezone set via timedatectl). On most cloud servers and containers, this is UTC by default. If you are on a server configured to UTC and want a job at 9 AM IST, you must specify 3:30 AM UTC in your cron expression. Always verify your server's timezone with 'date' or 'timedatectl status' before writing cron schedules.

What happens if a cron job is still running when the next scheduled time arrives?

Standard cron does not wait — it starts a new instance of the job at the scheduled time regardless of whether the previous run has finished. If the job takes longer than its interval, you can end up with multiple concurrent instances. This is a common source of resource exhaustion and data corruption. Solutions: use a lock file (flock), use a job scheduler that supports concurrency control (like Sidekiq or Celery Beat), or use 'run-one' wrapper on Linux systems.

Can I specify seconds in a cron expression?

Standard Unix/Linux cron does not support seconds — the minimum granularity is 1 minute. If you need sub-minute scheduling, use Quartz Cron (Java, supports 6 or 7 fields including seconds), Spring Scheduler (@Scheduled annotation), or platform-specific schedulers. AWS EventBridge Scheduler supports rate expressions like 'rate(30 seconds)' for sub-minute scheduling. For most use cases, 1-minute granularity is sufficient.

What does */5 actually mean in a cron field?

*/5 means 'every 5th value starting from the minimum'. In the minute field (0–59), */5 means 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55 — 12 times per hour. In the hour field (0–23), */5 means 0, 5, 10, 15, 20 — 5 times per day. The starting point is always the minimum of the field's range, not an arbitrary offset. If you want to start at an offset (e.g. every 5 minutes starting at minute 2: 2, 7, 12...), you need to use a list or a range with step: 2-59/5.

How do I run a cron job on the last day of the month?

Standard cron has no built-in 'last day of month' operator except in extended implementations. In Quartz Cron, use 'L' in the day-of-month field. In standard cron, the common workaround is to schedule for day 28-31 and include a shell check in the script: [ "$(date -d tomorrow +%d)" == "01" ] && your_command. This checks whether tomorrow is the 1st of the month, effectively running only on the last day. AWS EventBridge Scheduler also has no native last-day support — the same script workaround applies.

Why does my cron job run twice on the day DST ends?

When clocks fall back (DST ending), the same local hour occurs twice. If your cron is scheduled at 1:30 AM local time and DST ends at 2:00 AM (clocks revert to 1:00 AM), the 1:30 AM timeslot occurs twice — and cron will execute twice. The fix is to use UTC on your server, which has no DST transitions. If you cannot use UTC, be aware of this behaviour for jobs scheduled during the DST transition window (1:00–2:00 AM typically) and handle idempotency in your job logic.

What is the difference between cron on Linux and AWS EventBridge cron?

Linux cron uses the standard 5-field syntax (minute, hour, day-of-month, month, day-of-week). AWS EventBridge Scheduler uses a 6-field cron expression with an additional year field at the end: cron(minute hour day-of-month month day-of-week year). EventBridge also supports 'L' for last day, 'W' for nearest weekday, and '?' for unspecified. EventBridge schedules are always in UTC unless you configure a timezone. GitHub Actions cron syntax follows standard 5-field POSIX cron.

Related Concepts

Related Tools

Cron Expression Parser

Parse any cron expression and see the next 10 scheduled execution times with human-readable descriptions.

Open Tool →

Timestamp to Date Converter

Convert cron job log timestamps to readable dates — useful for debugging past runs.

Open Tool →

Time Zone Converter

Convert cron scheduled times between UTC and any local timezone including IST.

Open Tool →

World Clock

Check what time it is in UTC and multiple cities simultaneously when planning cron schedules.

Open Tool →