Understanding Cron Expressions: A Practical Guide
Learn cron syntax from the ground up. Covers the five-field format, common schedules, platform differences, and the mistakes that trip people up.
Cron expressions are the standard way to schedule recurring tasks on Unix-like systems, and they show up everywhere: CI/CD pipelines, database backups, report generation, cache invalidation, and more. The syntax is compact but not immediately intuitive. This guide breaks down how cron expressions work, walks through common scheduling patterns, and highlights the mistakes that catch people off guard.
The Five-Field Cron Format
A standard cron expression consists of five fields separated by spaces. Each field represents a unit of time, and together they define when a job should run.
┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-7, where 0 and 7 = Sunday) │ │ │ │ │ * * * * *
The asterisk * means "every possible value" for that field. So * * * * * runs every minute of every hour of every day. Here are the special characters you can use in each field:
| Character | Meaning | Example |
|---|---|---|
* | Every value | * * * * * (every minute) |
, | Multiple values | 0 9,17 * * * (9 AM and 5 PM) |
- | Range of values | 0 9-17 * * * (every hour, 9 AM to 5 PM) |
/ | Step values | */15 * * * * (every 15 minutes) |
Common Cron Schedules
These are the patterns you'll use most often. Bookmark this section for quick reference.
# Every 5 minutes */5 * * * * # Every hour at minute 0 0 * * * * # Every day at midnight 0 0 * * * # Every day at 2:30 AM (common for backups) 30 2 * * * # Every Monday at 9 AM 0 9 * * 1 # Weekdays at 8 AM 0 8 * * 1-5 # First day of every month at midnight 0 0 1 * * # Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight 0 0 1 1,4,7,10 * # Every 6 hours 0 */6 * * * # Twice a day at 8 AM and 8 PM 0 8,20 * * * # Last weekday cron workaround: run on the 28th-31st, check in script 0 0 28-31 * *
Not sure if your expression is correct? Paste it into the Cron Parser to see a human-readable description and the next several execution times.
Extended Cron Syntax (6 and 7 Fields)
Some systems support extended cron formats. The most common extension adds a seconds field at the beginning. Some implementations also add a year field at the end.
# 6-field format (with seconds) ┌───────────── second (0-59) │ ┌───────────── minute (0-59) │ │ ┌───────────── hour (0-23) │ │ │ ┌───────────── day of month (1-31) │ │ │ │ ┌───────────── month (1-12) │ │ │ │ │ ┌───────────── day of week (0-7) │ │ │ │ │ │ * * * * * * # Example: Every 30 seconds */30 * * * * * # 7-field format (with seconds and year) - used by some Java schedulers # second minute hour day-of-month month day-of-week year 0 0 12 * * ? 2026
Tools like Quartz (Java), node-cron, and Spring Scheduler use the 6-field format with seconds. AWS CloudWatch and GitHub Actions use the standard 5-field format. Always check which format your platform expects.
Cron in Different Environments
The syntax is mostly consistent across platforms, but there are important differences in how cron jobs are configured and managed.
Linux/Unix crontab
# Edit your crontab crontab -e # List current cron jobs crontab -l # Example entry: backup database every night at 2 AM 0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1 # Special strings (supported by most Linux cron daemons) @reboot /home/user/scripts/startup.sh # Run once at boot @hourly /home/user/scripts/cleanup.sh # Same as 0 * * * * @daily /home/user/scripts/report.sh # Same as 0 0 * * * @weekly /home/user/scripts/digest.sh # Same as 0 0 * * 0 @monthly /home/user/scripts/invoice.sh # Same as 0 0 1 * *
GitHub Actions
# .github/workflows/scheduled.yml
on:
schedule:
- cron: '0 6 * * 1-5' # Weekdays at 6 AM UTC
# Note: GitHub Actions cron is always in UTC
# Minimum interval is every 5 minutes
# Scheduled runs can be delayed during high-demand periodsKubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-cleanup
spec:
schedule: "0 3 * * *" # 3 AM daily
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: myapp:latest
command: ["python", "cleanup.py"]
restartPolicy: OnFailureBuild your cron schedule visually with the Crontab Generator, which lets you select time intervals with dropdowns and see the resulting expression.
Common Mistakes and How to Avoid Them
These are the errors that generate the most confusion and the most late-night debugging sessions.
- Forgetting about time zones: Cron runs in the system's local time zone by default. On servers, that's usually UTC. On your laptop, it's your local time. If your job runs at the wrong hour, check the time zone first.
- Day-of-month AND day-of-week conflict: When both fields have specific values (not *), most cron implementations run the job when either condition is met, not when both are met.
0 0 15 * 1runs on the 15th of every month AND every Monday, not just Mondays that fall on the 15th. - No output redirection: By default, cron emails the output of every job to the user. If email isn't configured, the output just disappears. Always redirect output to a log file:
>> /var/log/myjob.log 2>&1. - Environment variables missing: Cron jobs run with a minimal environment. Your
PATH, database credentials, and other environment variables from your shell profile won't be available. Use full paths to executables and set variables explicitly in the crontab or script. - Using
*/1instead of*: They mean the same thing.*/1is "every 1 minute," which is the same as "every minute." It works, but it's unnecessarily verbose.
Testing and Debugging Cron
The fastest way to verify a cron expression is to check when it will next run. Rather than waiting for the scheduled time and hoping for the best, use a parser that shows upcoming execution times.
You can also test your cron script manually before scheduling it. Run the command from the crontab entry directly in your terminal, using the same user account that cron will use. If it works interactively but fails in cron, the issue is almost certainly related to environment variables or file paths.
For quick conversions between cron and human-readable time, the Unix Timestamp Converter is handy for checking exact dates and times when debugging scheduling issues.
Whether you're setting up a nightly backup or scheduling a weekly report, getting the cron expression right the first time saves you from the classic "wait until 2 AM to see if it works" debugging cycle. Use the Cron Parser to validate your expressions before deploying them.