Cron Expressions Explained (With Examples You'll Actually Use)
Learn how cron expressions work, what each field means, common cron examples, special characters, shortcuts, and the mistakes that cause scheduled jobs to run at the wrong time.
Cron expressions look like someone dropped a handful of punctuation into a terminal.
0 2 * * *
Yet that tiny line can schedule a database backup, send a report, rotate logs, sync data, clear expired sessions, or run a health check every night at 2 AM.
Cron has stayed popular because the format is compact, portable, and good enough for a huge number of recurring jobs. The hard part is learning to read it without guessing.
Once you understand the five fields, most cron expressions become surprisingly readable.
What Is a Cron Expression?
A cron expression is a compact schedule format used to run jobs at recurring times.
In standard Unix cron, the expression has five time fields:
minute hour day-of-month month day-of-week
Each field controls one part of the schedule.
* * * * *
| | | | |
| | | | +-- day of week
| | | +---- month
| | +------ day of month
| +-------- hour
+---------- minute
The classic Linux crontab format then adds the command after those five fields:
0 2 * * * /usr/local/bin/backup.sh
That means:
Run /usr/local/bin/backup.sh
at minute 0
of hour 2
on every day
of every month
on every weekday
In plain English: run every day at 2:00 AM.
The Debian crontab manual describes this standard format as five time and date fields followed by a command.
The Five Cron Fields
Standard cron fields are read from left to right:
| Field | Meaning | Allowed Values |
|---|---|---|
| Minute | Minute of the hour | 0-59 |
| Hour | Hour of the day | 0-23 |
| Day of month | Day number | 1-31 |
| Month | Month number or name | 1-12 or JAN-DEC |
| Day of week | Weekday number or name | 0-7 or SUN-SAT |
Both 0 and 7 usually mean Sunday in the day-of-week field.
The hour field uses 24-hour time. So 14 means 2 PM, not 2 AM.
What the Asterisk Means
An asterisk means “every valid value for this field.”
* * * * *
This means:
Every minute
of every hour
of every day of the month
of every month
on every day of the week
In other words: run every minute.
That is the most frequent schedule standard cron can express, because traditional cron checks schedules once per minute.
Common Cron Examples
These are the expressions people actually reach for most often.
| Cron Expression | Meaning |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/15 * * * * | Every 15 minutes |
0 * * * * | Every hour |
0 9 * * * | Every day at 9:00 AM |
0 2 * * * | Every day at 2:00 AM |
30 6 * * * | Every day at 6:30 AM |
0 0 * * * | Every day at midnight |
0 0 * * 0 | Every Sunday at midnight |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 9 * * MON-FRI | Weekdays at 9:00 AM |
0 12 1 * * | Noon on the first day of every month |
0 0 1 1 * | Midnight on January 1 every year |
If you only memorize a few, memorize these:
*/5 * * * * every 5 minutes
0 * * * * every hour
0 0 * * * every day at midnight
0 9 * * 1-5 weekdays at 9 AM
Every 5 Minutes
*/5 * * * *
This uses a step value.
The first field is minute, so */5 means every fifth minute:
00, 05, 10, 15, 20, ...
Use this for jobs like:
- Polling an API
- Processing a small queue
- Checking service health
- Refreshing short-lived cache data
Be careful with jobs that can run longer than five minutes. Cron may start a new run before the previous one finishes unless you add locking or use a scheduler that prevents overlap.
Every Hour
0 * * * *
This runs at minute 0 of every hour:
00:00
01:00
02:00
03:00
...
Use this for:
- Hourly summaries
- Cache warming
- Rate-limit reset checks
- Import jobs that do not need constant polling
Do not write * */1 * * * when you mean hourly. That runs every minute during every hour, which is just every minute.
Every Day at 2 AM
0 2 * * *
This is a classic backup schedule.
It runs:
minute: 0
hour: 2
day of month: every
month: every
day of week: every
Use this for:
- Database backups
- Daily exports
- Cleanup jobs
- Rebuilding search indexes
- Sending daily digests
Daily jobs should log clearly when they start, finish, and fail. For logging guidance, see log levels: when to use debug, info, warn, and error.
Weekdays at 9 AM
0 9 * * 1-5
This means:
At 09:00
on Monday through Friday
You may also see:
0 9 * * MON-FRI
This is useful for:
- Business-hour reports
- Workday reminders
- Office-hour sync jobs
- Weekday-only notifications
Use weekday names if your cron implementation supports them and the expression will be read by humans. MON-FRI is often clearer than 1-5.
Once a Month
0 0 1 * *
This runs at midnight on the first day of every month.
Use this for:
- Monthly invoices
- Monthly usage reports
- Subscription reconciliation
- Data retention jobs
- Billing period setup
If the business rule is “last day of the month,” standard cron alone is awkward because months have different lengths. Some platforms support extensions like L, but standard Unix cron does not. In portable cron, schedule a daily job and let the script decide whether today is the last day of the month.
Once a Year
0 0 1 1 *
This runs at midnight on January 1.
Use this for:
- Annual reporting
- Yearly archive creation
- Resetting yearly counters
- Sending yearly maintenance reminders
For jobs this infrequent, monitoring matters. If a yearly job silently fails, nobody may notice until a year later. Add logs, alerts, and an easy manual rerun path.
Lists, Ranges, and Steps
Cron fields support a small pattern language.
Lists
Use commas to specify exact values:
0 9,12,17 * * *
Run at 9 AM, noon, and 5 PM every day.
Ranges
Use hyphens to specify inclusive ranges:
0 9 * * 1-5
Run at 9 AM Monday through Friday.
Steps
Use slash syntax to specify intervals:
*/10 * * * *
Run every 10 minutes.
You can also combine ranges and steps:
*/15 9-17 * * 1-5
Run every 15 minutes during 9 AM through 5 PM on weekdays.
This is compact, but do not let cleverness beat readability. Cron expressions are like regular expressions in that they are powerful because they compress a pattern into a small string. That same compression can make mistakes harder to spot. For a related pattern-language mindset, see what is a regular expression?.
Special Cron Shortcuts
Many cron implementations support shortcuts:
| Shortcut | Equivalent | Meaning |
|---|---|---|
@hourly | 0 * * * * | Once an hour |
@daily | 0 0 * * * | Once a day at midnight |
@midnight | 0 0 * * * | Same as @daily |
@weekly | 0 0 * * 0 | Once a week |
@monthly | 0 0 1 * * | Once a month |
@yearly | 0 0 1 1 * | Once a year |
@annually | 0 0 1 1 * | Same as @yearly |
@reboot | not time-based | Once when cron starts |
These are readable and usually worth using when they match the schedule exactly.
For example:
@daily /usr/local/bin/nightly-cleanup.sh
is easier to scan than:
0 0 * * * /usr/local/bin/nightly-cleanup.sh
Use @reboot carefully. It runs when the cron daemon starts, not necessarily when every dependency your job needs is ready.
Day of Month vs Day of Week
This is one of the easiest cron mistakes to miss.
In many cron implementations, if both day-of-month and day-of-week are restricted, the job runs when either field matches.
Example:
30 4 1,15 * 5
This does not mean:
Run at 4:30 AM
only when the 1st or 15th is a Friday
It usually means:
Run at 4:30 AM
on the 1st and 15th of the month
and every Friday
The Debian and Linux man pages both call out this behavior. It surprises people because they expect the two day fields to behave like an AND condition. Standard cron often treats them like OR.
If you need “the first Friday of the month,” use a daily or weekly schedule plus a check inside the script.
Cron Time Zones
Cron usually runs in the time zone of the system or cron daemon unless configured otherwise.
That means:
0 9 * * *
does not inherently mean 9 AM UTC, 9 AM New York time, or 9 AM Auckland time. It means 9 AM in whatever time zone the scheduler uses.
This matters for:
- User-facing emails
- Billing jobs
- Global products
- Daylight saving time changes
- Multi-region deployments
- Kubernetes and cloud schedulers
If the job has business meaning, document the intended time zone. If possible, set it explicitly in the scheduler or run the job in UTC and convert inside application logic.
Daylight Saving Time Problems
Daylight saving time can make cron behavior surprising.
When clocks jump forward, some local times do not exist. A job scheduled during that missing hour may not run.
When clocks fall back, some local times happen twice. A job scheduled during that repeated hour may run twice.
The Linux crontab manual notes this exact issue for non-existent and repeated times during daylight saving changes.
For critical jobs, prefer UTC or add idempotency so duplicate runs are safe.
Idempotency matters for scheduled jobs in the same way it matters for distributed workflows: retries and repeated events should not corrupt data. The lifecycle modeling in what is a state machine? is useful when a scheduled job moves records through states like queued, processing, completed, and failed.
Five-Field vs Six-Field Cron
Standard Unix cron uses five schedule fields:
minute hour day-of-month month day-of-week
Some systems use six or seven fields.
For example, Quartz-style cron expressions include seconds, and may include year:
seconds minutes hours day-of-month month day-of-week year
That means this expression:
0 0 2 * * ?
is not standard Unix cron. In Quartz-like systems, it means 2 AM every day because the first 0 is seconds.
This is why cron expressions should never be copied blindly between platforms. GitHub Actions, Kubernetes CronJobs, Linux crontab, Quartz, Spring, AWS EventBridge, and cloud schedulers may have small but important differences.
Quartz’s CronTrigger documentation shows these extra fields and special characters such as ?, L, W, and #, which are not portable to every cron implementation.
Cron in Kubernetes and Cloud Schedulers
Cron syntax appears outside traditional Linux servers.
You may see it in:
- Kubernetes
CronJob - GitHub Actions schedules
- AWS EventBridge
- Google Cloud Scheduler
- Azure scheduled jobs
- CI/CD pipelines
- Serverless functions
The syntax may look familiar, but behavior can differ:
- Time zone defaults
- Minimum interval
- Missed run handling
- Concurrent run policy
- Retry behavior
- Seconds field support
- Special character support
Always check the scheduler’s documentation before assuming one cron expression behaves the same everywhere.
Useful Cron Examples
Here are examples you can actually reuse.
Every 10 Minutes
*/10 * * * *
Good for lightweight polling or queue checks.
Every 30 Minutes
*/30 * * * *
Runs at minute 0 and 30 of every hour.
At the Start of Every Hour
0 * * * *
Good for hourly aggregates.
Every Day at 6:30 AM
30 6 * * *
Good for morning summaries.
Every Night at 11 PM
0 23 * * *
Good for end-of-day cleanup.
Every Monday at 8 AM
0 8 * * 1
Good for weekly team reports.
Every Friday at 5 PM
0 17 * * 5
Good for weekly wrap-up reports.
Weekdays Every 15 Minutes During Business Hours
*/15 9-17 * * 1-5
Good for office-hour sync jobs.
First Day of Every Month at Midnight
0 0 1 * *
Good for monthly billing setup.
January 1 at Midnight
0 0 1 1 *
Good for yearly archive jobs.
Production Cron Job Checklist
Before putting a cron job into production, check:
- Does the expression run at the intended time?
- Is the time zone explicit?
- What happens if the job runs twice?
- What happens if the previous run is still active?
- Are failures logged?
- Are important failures alerted?
- Can the job be rerun manually?
- Does the job have the environment variables it needs?
- Does it use absolute paths?
- Does it handle partial success safely?
- Is output captured somewhere useful?
Cron is good at starting commands. It is not a full workflow engine, retry system, queue, or observability platform by itself.
For multi-step workflows with state, retries, and compensation, an explicit orchestrator may be better. See the orchestrator pattern for that broader design.
Logging Cron Jobs
Scheduled jobs need logs because nobody is sitting there watching them run.
At minimum, log:
- Job started
- Job finished
- Duration
- Number of records processed
- Skipped or failed records
- External dependencies called
- Final status
Example:
{
"level": "info",
"service": "billing-worker",
"job": "monthly-invoice-generation",
"durationMs": 8421,
"processed": 2380,
"failed": 3,
"message": "Monthly invoice generation completed"
}
Structured logs make scheduled jobs much easier to search, alert on, and correlate with other systems. See JSON logging best practices and correlation ID vs trace ID for the observability side.
Common Cron Mistakes
Using * in the minute field accidentally. * 2 * * * runs every minute during the 2 AM hour, not once at 2 AM. Use 0 2 * * *.
Forgetting 24-hour time. 0 14 * * * is 2 PM. 0 2 * * * is 2 AM.
Assuming day-of-month and day-of-week are ANDed. Many cron implementations run when either field matches if both are restricted.
Ignoring time zones. A job that runs at 9 AM on one server may run at a different business time after migration.
Ignoring daylight saving time. Local-time schedules can be skipped or duplicated during DST changes.
Copying Quartz cron into Unix cron. Six-field and seven-field expressions may not work in standard crontab.
Not handling overlap. Cron can start another run before the previous one finishes.
Using relative paths. Cron jobs often run with a limited environment. Use absolute paths and set required variables.
Related Reading
If you are working with scheduled jobs and automation, try these topics:
- Log levels: when to use debug, info, warn, and error: how scheduled jobs should report success and failure
- JSON logging best practices: how to make cron job logs searchable
- Correlation ID vs trace ID: how to connect scheduled work to downstream requests
- What is a state machine?: useful when jobs move records through lifecycle states
- The orchestrator pattern: when cron is too small for the workflow
- What is a regular expression?: another compact pattern language developers often need to read carefully
- What is a .dockerignore file?: useful when scheduled jobs run inside containers and build context matters
For external references, start with the Debian crontab manual, the Linux crontab manual page, and Quartz’s CronTrigger tutorial for six-field and seven-field variants.
Frequently Asked Questions
What does * * * * * mean in cron?
It means every minute. Each asterisk means every valid value for that field, so all five asterisks together match every minute of every hour of every day.
How do I run a cron job every day at 2 AM?
Use 0 2 * * *. The first field sets minute 0, and the second field sets hour 2.
How do I run a cron job every 5 minutes?
Use */5 * * * *. The */5 in the minute field means every fifth minute.
What is the difference between five-field and six-field cron? Standard Unix cron uses five schedule fields: minute, hour, day of month, month, and day of week. Some schedulers, such as Quartz-style systems, add a seconds field at the beginning and sometimes a year field at the end.
Do cron jobs use UTC? Not always. Traditional cron usually uses the system or daemon time zone unless configured otherwise. Cloud schedulers may use UTC by default or allow explicit time zones. Always check the scheduler you are using.
Conclusion
Cron expressions are compact schedules made from five standard fields: minute, hour, day of month, month, and day of week. Once you learn that order, expressions like 0 2 * * *, */15 * * * *, and 0 9 * * 1-5 become much easier to read.
The main risks are not the simple examples. They are the edge cases: time zones, daylight saving time, overlapping runs, platform-specific syntax, and the surprising behavior of day-of-month combined with day-of-week.
Use cron for simple recurring jobs. Add logging, idempotency, monitoring, and explicit time zone decisions for production. When the workflow becomes multi-step, stateful, or failure-sensitive, cron can still start the work, but a real workflow or orchestration system should probably own the process.
Written by the Workshelve team, who write practical explainers on data integrity, networking, and developer tooling.