Cron expressions are the universal language of scheduled tasks. Whether you are setting up a database backup on Linux, scheduling a Kubernetes CronJob, configuring a CI/CD pipeline, or automating a cloud function, you will need to write cron expressions. The syntax is compact but not intuitive — this guide breaks it down field by field with practical examples you can use immediately.
The Five Fields
A standard cron expression has five fields separated by spaces, read from left to right:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, where 0 and 7 are Sunday)
│ │ │ │ │
* * * * *
Each field accepts specific values, ranges, lists and special characters. The expression fires when all five fields match the current time simultaneously.
Special Characters
| Character | Meaning | Example |
|---|---|---|
* | Any value | * * * * * runs every minute |
, | List of values | 1,15 * * * * runs at minute 1 and 15 |
- | Range of values | 0 9-17 * * * runs hourly from 9am to 5pm |
/ | Step values | */15 * * * * runs every 15 minutes |
Some implementations support additional characters: L for last (last day of month), W for nearest weekday, and # for nth weekday of month (e.g. 5#2 = second Friday). These are non-standard extensions found in Quartz scheduler, Spring, and some cloud platforms but not in standard Unix cron.
20 Practical Examples
Basic Schedules
# Every minute
* * * * *
# Every hour at minute 0
0 * * * *
# Every day at midnight
0 0 * * *
# Every day at 9:30am
30 9 * * *
# Every Monday at 8am
0 8 * * 1
Business Schedules
# Every weekday at 9am (Mon-Fri)
0 9 * * 1-5
# Every 15 minutes during business hours
*/15 9-17 * * 1-5
# First day of every month at midnight
0 0 1 * *
# Every quarter (Jan, Apr, Jul, Oct) on the 1st
0 0 1 1,4,7,10 *
# Last working day — approximate (28th)
0 18 28 * *
Maintenance and Operations
# Database backup every day at 2am
0 2 * * *
# Log rotation every Sunday at 3am
0 3 * * 0
# Health check every 5 minutes
*/5 * * * *
# Cache clear twice daily
0 6,18 * * *
# Weekly report every Friday at 5pm
0 17 * * 5
# Cleanup job at 3:30am on weekends only
30 3 * * 6,0
Build Cron Expressions Visually
Select schedule parameters from dropdowns and see the cron expression generated in real time with next run preview.
Open Cron Builder →Common Mistakes
Confusing day-of-month and day-of-week: The fourth and fifth fields are month and day-of-week respectively. 0 0 * * 5 means every Friday, not the 5th of every month. For the 5th of every month, use 0 0 5 * *.
Step values starting from zero: */15 * * * * runs at minutes 0, 15, 30, 45 — not at 15-minute intervals from when the cron was created. If you start a cron at 10:07, the next run is still at 10:15, not 10:22.
Timezone assumptions: Unix cron uses the system timezone. Kubernetes CronJobs use UTC by default. Cloud scheduler services vary — AWS EventBridge uses UTC, Google Cloud Scheduler lets you specify a timezone. Always verify which timezone your cron system uses, especially for schedules that cross midnight or involve daylight saving transitions.
Overlap risk: If a job takes longer to run than the interval between runs, you can end up with multiple instances running simultaneously. A backup job scheduled every hour that takes 90 minutes to complete will overlap. Most cron systems do not prevent this automatically — use a lock file, process check, or concurrency policy to guard against it.
Month and weekday names: Some implementations accept names like MON or JAN instead of numbers. This improves readability but is not universal. Standard Unix cron only accepts numbers. Use names in Kubernetes, Quartz and Spring; use numbers everywhere else.
Cron in Different Systems
Linux/Unix Crontab
Edit with crontab -e. Each line is an expression followed by the command:
# Backup database every night at 2am
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Environment variables
MAILTO=admin@example.com
PATH=/usr/local/bin:/usr/bin:/bin
# Run as specific user (system crontab only)
0 2 * * * postgres /usr/local/bin/pg_backup.sh
The 2>&1 redirect ensures both stdout and stderr are captured in the log. Without this, error output vanishes silently — one of the most common reasons cron jobs appear to fail without explanation.
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
restartPolicy: OnFailure
Kubernetes CronJobs add a concurrencyPolicy field that standard cron lacks — set it to Forbid to prevent overlapping runs, or Replace to kill the previous run when a new one starts.
GitHub Actions
on:
schedule:
- cron: '0 6 * * 1-5' # Weekdays at 6am UTC
GitHub Actions cron runs in UTC and has a minimum interval of 5 minutes. Scheduled workflows may be delayed during periods of high demand on GitHub infrastructure.
Cloud Platforms
AWS EventBridge, Google Cloud Scheduler and Azure Logic Apps all support cron expressions but with slight variations. AWS adds a sixth field for year and uses ? for unspecified day-of-month or day-of-week. Google Cloud Scheduler follows standard five-field syntax with timezone support. Always check your platform's specific cron documentation.
Six-Field and Seven-Field Expressions
Some systems extend the standard five-field format:
| System | Fields | Extra Fields |
|---|---|---|
| Standard Unix | 5 | None |
| Quartz / Spring | 6-7 | Seconds (prepended), Year (appended, optional) |
| AWS EventBridge | 6 | Year (appended) |
| Kubernetes | 5 | None |
The seconds field in Quartz allows sub-minute scheduling like 0/30 * * * * ? for every 30 seconds. If you see a cron expression with six fields and the first is 0, it is likely a Quartz expression where the first field is seconds, not minutes.
Debugging Failed Cron Jobs
Cron jobs fail silently more often than any other scheduled system. Here is a systematic approach to diagnosing issues:
Check the basics first: Is the cron daemon running? On Linux, run systemctl status cron (Debian/Ubuntu) or systemctl status crond (RHEL/CentOS). A stopped daemon means nothing executes regardless of your expressions.
Examine the mail: By default, cron sends output to the user's local mail. Run mail or check /var/mail/username. Many failed crons have been sending error messages to local mail that nobody reads. Set MAILTO=you@example.com at the top of your crontab to redirect these to an actual inbox.
Environment differences: This is the most common source of confusion. Cron runs with a minimal environment — typically only HOME, LOGNAME, PATH=/usr/bin:/bin, and SHELL=/bin/sh. A script that works perfectly when you run it manually might fail under cron because PATH doesn't include /usr/local/bin, environment variables from .bashrc aren't loaded, or the working directory is different. Always use absolute paths in cron scripts and explicitly set any required environment variables at the top of the crontab or within the script itself.
Permission problems: Verify the cron user has execute permission on the script (chmod +x) and read/write access to any files the script touches. Also check /etc/cron.allow and /etc/cron.deny — if cron.allow exists and your user is not listed, crontab commands are denied entirely.
Logging everything: Add explicit logging to every cron job. At minimum, capture both stdout and stderr with a timestamp:
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup-$(date +\%Y\%m\%d).log 2>&1
Note the escaped percent signs — in crontab, % is a special character that becomes a newline. Use \% for literal percent signs inside cron expressions.
Testing and Validation
Never deploy a cron expression without testing it first. A mistake in a production cron can mean a job runs every minute instead of every month — potentially overloading systems, sending thousands of duplicate emails, or running expensive cloud functions non-stop. This is especially dangerous with cloud functions where each invocation costs money.
Use a cron expression tool to preview the next several run times before deploying. Our cron builder shows the next 5 scheduled runs so you can verify the schedule matches your intent. On the command line, crontab -l lists your current cron jobs, and tools like cronnext can preview upcoming executions.
For critical production crons, add monitoring. If a backup job should run nightly, set up an alert that fires if the job has not completed successfully in the last 25 hours. Healthchecks.io and Cronitor are purpose-built for cron monitoring — they give each cron job a unique URL to ping on completion, and alert you via email, Slack or PagerDuty if the ping does not arrive on schedule. This is far more reliable than manually checking logs.
Build Cron Expressions Visually
Select your schedule, see the expression, and preview next runs — all in your browser.
Open Cron Builder →