What is CRON?
CRON is the standard job scheduler on Unix-like operating systems. It has been part of Unix since the late 1970s, and its syntax has become the universal language for expressing recurring schedules. Whether you need to run a database backup every night, clear temporary files every hour, or send a weekly report every Monday morning, CRON is how you tell the system when to do it.
The cron daemon (crond) runs in the background and checks a table of scheduled commands — the crontab — once per minute. When the current time matches a schedule entry, cron executes the associated command. There is no polling, no custom loop, and no code to maintain. You write the schedule, and cron handles the rest.
Today, CRON expressions are used far beyond traditional Unix systems. GitHub Actions, AWS EventBridge, Kubernetes CronJobs, and dozens of CI/CD platforms all accept cron syntax to define schedules. Understanding how to read and write these five-field expressions is a fundamental skill for any developer or system administrator.
Cron Expression Cheat Sheet
Before diving into the deep mechanics, here is a quick-reference cheat sheet of the most common cron expressions developers actually use in production. Copy any row directly into your crontab, GitHub Actions workflow, or Kubernetes CronJob manifest.
| Expression | Meaning | Example Use Case |
|---|---|---|
* * * * * |
Every minute | Health checks, heartbeat pings |
*/5 * * * * |
Every 5 minutes | Polling an external API |
*/15 * * * * |
Every 15 minutes | Cache refresh, queue draining |
*/30 * * * * |
Every 30 minutes | Sync jobs, metric aggregation |
0 * * * * |
Every hour at minute 0 | Hourly log rotation |
0 */2 * * * |
Every 2 hours | Mid-frequency batch processing |
0 0 * * * |
Every day at midnight | Nightly database backup |
0 12 * * * |
Every day at noon | Daily summary email |
0 9 * * 1-5 |
Weekdays at 9:00 AM | Morning report for the team |
0 17 * * 5 |
Every Friday at 5:00 PM | Weekly wrap-up notification |
0 0 * * 0 |
Every Sunday at midnight | Weekly maintenance window |
0 0 * * 1 |
Every Monday at midnight | Start-of-week reset job |
0 0 1 * * |
First day of every month at midnight | Monthly invoicing, billing runs |
0 0 15 * * |
15th of every month at midnight | Mid-month payroll job |
0 0 1 */3 * |
First day of every quarter | Quarterly reports |
0 0 1 1 * |
Once a year on January 1st | Annual cleanup, archive rotation |
0 2 * * * |
Every day at 2:00 AM | Off-peak heavy compute jobs |
0 6,18 * * * |
Twice daily at 6 AM and 6 PM | Bi-daily sync |
Cron Field Reference
Every cron expression in the standard Unix format has exactly five fields, always in the same order. This reference table summarizes each field's position, allowed values, and which special characters it accepts.
| Field | Position | Allowed Values | Special Characters |
|---|---|---|---|
| Minute | 1st | 0–59 |
* , - / |
| Hour | 2nd | 0–23 |
* , - / |
| Day of Month | 3rd | 1–31 |
* , - / |
| Month | 4th | 1–12 or JAN–DEC |
* , - / |
| Day of Week | 5th | 0–7 or SUN–SAT |
* , - / |
Note that the day-of-week field accepts both 0 and 7 for Sunday, and many implementations also accept three-letter abbreviations like MON, TUE, etc. The month field similarly accepts JAN through DEC.
The 5 Fields
A standard CRON expression consists of exactly five fields separated by spaces. Each field represents a unit of time and accepts a specific range of values:
| Field | Allowed Values | Description |
|---|---|---|
| Minute | 0–59 |
The minute of the hour when the job runs |
| Hour | 0–23 |
The hour of the day (24-hour format) |
| Day of Month | 1–31 |
The day of the month |
| Month | 1–12 |
The month of the year |
| Day of Week | 0–7 |
The day of the week (0 and 7 are both Sunday) |
The fields are always in this order: minute, hour, day of month, month, day of week. Here is an annotated example:
30 9 * * 1-5
│ │ │ │ │
│ │ │ │ └── Day of week: 1-5 (Monday through Friday)
│ │ │ └──── Month: * (every month)
│ │ └────── Day of month: * (every day)
│ └──────── Hour: 9 (9 AM)
└────────── Minute: 30
This expression means: at 9:30 AM, Monday through Friday. It runs once per day on weekdays — a typical schedule for a morning report or notification.
Special Characters
CRON expressions support four special characters that give you flexible control over scheduling:
*(asterisk) — any value. Matches every possible value in the field.* * * * *means every minute of every hour of every day. When you see an asterisk, read it as "every.",(comma) — list. Specifies multiple discrete values.0,15,30,45 * * * *runs at minutes 0, 15, 30, and 45 of every hour. You can list as many values as you need, separated by commas.-(hyphen) — range. Specifies a continuous range of values.* 9-17 * * *runs every minute from 9:00 AM through 5:59 PM. The range is inclusive on both ends./(slash) — step. Specifies an interval.*/10 * * * *means every 10th minute (0, 10, 20, 30, 40, 50). You can combine it with a range:1-30/5 * * * *runs at minutes 1, 6, 11, 16, 21, and 26.
These characters can be combined within a single field. For example, 0,30 9-17 * * 1-5 runs at the top and bottom of every hour during business hours on weekdays — useful for polling an API or syncing data during the workday.
10 Common CRON Schedules
Here are ten CRON expressions that cover the most frequently needed schedules. You can copy these directly into your crontab or CI/CD configuration:
| Expression | Description |
|---|---|
* * * * * |
Every minute |
*/5 * * * * |
Every 5 minutes |
0 * * * * |
Every hour (at minute 0) |
0 0 * * * |
Every day at midnight |
0 9 * * 1-5 |
Every weekday at 9:00 AM |
0 0 * * 0 |
Every Sunday at midnight |
0 0 1 * * |
First day of every month at midnight |
0 6,18 * * * |
Twice daily at 6:00 AM and 6:00 PM |
30 2 * * 0 |
Every Sunday at 2:30 AM (maintenance window) |
0 0 1 1 * |
Once a year on January 1st at midnight |
Most scheduling needs can be met by adapting one of these patterns. Change the hour, swap the day-of-week range, or add a step value to adjust the frequency.
Common Mistakes
Cron syntax looks deceptively simple, but a handful of mistakes show up over and over in production incidents. Here are the most common ones to watch for when writing or reviewing a cron expression.
Confusing day-of-week numbering (0 vs 7 for Sunday). The day-of-week field accepts both 0 and 7 for Sunday, which is a frequent source of bugs. Some developers assume 1 is Sunday (like JavaScript's Date.getDay()), but in cron, 1 is Monday. If you mean Sunday, use 0 consistently. Worse, a few non-standard cron implementations like Quartz use 1–7 with 1 as Sunday — always check the docs for the platform you are targeting.
Forgetting timezone considerations. A cron expression is just numbers — it has no inherent timezone. The expression 0 9 * * * means 9:00 AM in whatever timezone the cron daemon is configured to use. On a Linux server that defaults to local time, this might be Eastern; on GitHub Actions, it is UTC; on AWS EventBridge, also UTC. Always set CRON_TZ explicitly when timing matters, especially around daylight saving time transitions.
Using 6-field vs 5-field cron (e.g. AWS vs Unix). Standard Unix cron uses 5 fields (minute through day-of-week). Some platforms add a 6th field — Quartz adds seconds at the front, AWS EventBridge adds a year at the end. Pasting a 5-field expression into a system that expects 6 fields (or vice versa) silently produces a different schedule than you intended. Always confirm the field count for your platform before copying expressions from one place to another.
The difference between */5 and 0/5. In standard Unix cron these are equivalent — both mean "every 5 minutes starting at 0." But in some implementations and especially in Quartz-based schedulers, 0/5 means "starting at minute 0, then every 5 minutes," while */5 is interpreted as a step over the full range. The result is usually identical, but in edge cases involving offset start values like 3/5 (every 5 minutes starting at minute 3), the behavior diverges. Stick with */N for portability.
Edge Cases to Watch
CRON expressions are straightforward once you understand the five fields, but a few edge cases catch people off guard. Knowing these will save you from debugging schedules that do not fire when you expect.
Day of week: 0 vs 7. Both 0 and 7 represent Sunday. The POSIX standard defines Sunday as 0, with Monday through Saturday as 1 through 6. Many cron implementations added 7 as an alias for Sunday because it feels more natural after 6 (Saturday). To avoid confusion, pick one and be consistent — most people use 0 for Sunday.
February and day-of-month limits. If you schedule a job for day 31 (0 0 31 * *), it will not run in months with fewer than 31 days. February never has more than 29 days, so 0 0 30 2 * will never execute. If you intend to run a job on the last day of every month, cron alone cannot express this — you will need a wrapper script that checks the date, or use a platform-specific extension.
Timezone awareness. Traditional Unix cron uses the system's local timezone. This means a server set to US Eastern time will interpret 0 9 * * * as 9:00 AM Eastern, which shifts relative to UTC during daylight saving time. Cloud platforms typically default to UTC. If your schedule is time-sensitive, always set the timezone explicitly using the CRON_TZ variable at the top of your crontab:
CRON_TZ=America/New_York
0 9 * * 1-5 /usr/local/bin/send-report.sh
Day-of-month AND day-of-week. When both the day-of-month and day-of-week fields are set to non-wildcard values, most cron implementations run the job when either condition is met, not when both are met. For example, 0 0 15 * 5 runs at midnight on the 15th of every month and on every Friday — not just on Fridays that fall on the 15th. This is one of the most commonly misunderstood behaviors in cron.
CRON in Different Environments
The five-field CRON syntax originated in Unix, but it has been adopted by many modern platforms. Each environment has its own quirks and extensions.
Linux crontab
The classic way to manage cron jobs. Edit your personal crontab with crontab -e and list entries with crontab -l. System-wide jobs go in /etc/crontab or as files in /etc/cron.d/. The system crontab format adds a sixth field for the username the command should run as:
# /etc/crontab format (note the username field)
0 3 * * * root /usr/local/bin/backup.sh
# User crontab format (no username field)
0 3 * * * /home/user/backup.sh
GitHub Actions
GitHub Actions uses the schedule trigger with standard five-field cron syntax. All schedules run in UTC and there is no way to change the timezone. GitHub may delay scheduled workflows during periods of high load, so they are not suitable for time-critical tasks:
on:
schedule:
- cron: '30 9 * * 1-5' # 9:30 AM UTC, weekdays
AWS EventBridge (CloudWatch Events)
AWS uses a six-field cron format that adds a year field and replaces some features. AWS cron uses ? (no specific value) for either day-of-month or day-of-week — you must use ? in one of the two fields. All schedules run in UTC by default:
# AWS EventBridge cron (6 fields, note the ? and year)
cron(30 9 ? * MON-FRI *) # 9:30 AM UTC, weekdays
Kubernetes CronJob
Kubernetes CronJobs use the standard 5-field cron schedule format — the same minute, hour, day-of-month, month, day-of-week layout you would find in a Unix crontab. There is no extra seconds field, no year field, and no Quartz-style extensions. If you can write a Unix cron expression, you can write a Kubernetes CronJob schedule. The .spec.schedule field accepts the expression as a quoted string, and the controller parses it with the standard Go cron library.
The schedule is evaluated in the timezone of the kube-controller-manager process, which on most clusters is UTC. Starting with Kubernetes 1.27, the .spec.timeZone field on the CronJob spec accepts an IANA timezone name (for example America/New_York) so you can pin the schedule to a specific timezone without having to convert to UTC manually. This is the recommended approach for any Kubernetes CronJob whose timing matters across daylight saving transitions:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
command: ["/bin/sh", "-c", "run-backup.sh"]
restartPolicy: OnFailure
Regardless of the platform, the core five-field syntax is the same. Learn it once, and you can schedule jobs anywhere.