Cron is the time-based job scheduler in Unix-like systems. It uses a compact expression format to specify when commands should run—every minute, every day at midnight, every Monday at 3 AM, or countless other schedules. This guide demystifies cron syntax so you can schedule any job with confidence.
Key Takeaways
- 1Standard cron has 5 fields: minute, hour, day-of-month, month, day-of-week
- 2Special characters: * (every), , (list), - (range), / (step), L (last), # (nth)
- 3Common patterns: */5 * * * * (every 5 min), 0 0 * * * (daily midnight), 0 9 * * 1-5 (weekdays 9 AM)
- 4Debug with absolute paths, output redirection, and checking cron logs
- 5Use flock to prevent overlapping runs and log everything with timestamps
Cron Expression Anatomy
A standard cron expression has 5 fields, each representing a time unit. Some systems (like Quartz) add a 6th field for seconds. Here's the basic format:
# Standard 5-field cron format
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * * command_to_execute| Field | Allowed Values | Special Characters |
|---|---|---|
| Minute | 0-59 | * , - / |
| Hour | 0-23 | * , - / |
| Day of Month | 1-31 | * , - / ? L W |
| Month | 1-12 or JAN-DEC | * , - / |
| Day of Week | 0-6 or SUN-SAT | * , - / ? L # |
Day of week: 0 and 7 both represent Sunday. Some systems accept 3-letter abbreviations (MON, TUE, etc.) for months and days.
2Special Characters Explained
Beyond simple numbers, cron uses special characters to create flexible schedules.
| Character | Meaning | Example |
|---|---|---|
| * | Any/every value | * in hour = every hour |
| , | List of values | 1,15,30 = 1st, 15th, 30th |
| - | Range of values | 1-5 = 1, 2, 3, 4, 5 |
| / | Step/interval | */15 = every 15 units |
| ? | No specific value (day fields) | Use ? when day-of-month OR day-of-week is set |
| L | Last (day fields) | L in day-of-month = last day of month |
| W | Nearest weekday | 15W = weekday nearest to 15th |
| # | Nth occurrence | 2#3 = 3rd Monday (day 2, 3rd occurrence) |
Example: Step Values in Action
Scenario
*/10 * * * * means run every 10 minutes (0, 10, 20, 30, 40, 50)
Solution
The */10 in minute field divides 60 by 10, running at each step.
Not all cron implementations support all special characters. Standard cron (crontab) supports * , - /. Extended features like L, W, # are available in Quartz, Spring, and other schedulers.
Common Scheduling Patterns
Here are the most frequently used cron expressions. Copy these as starting points for your schedules.
| Expression | Schedule |
|---|---|
| * * * * * | Every minute |
| */5 * * * * | Every 5 minutes |
| 0 * * * * | Every hour (at minute 0) |
| 0 0 * * * | Daily at midnight |
| 0 9 * * * | Daily at 9:00 AM |
| 0 9 * * 1-5 | Weekdays at 9:00 AM |
| 0 0 * * 0 | Every Sunday at midnight |
| 0 0 1 * * | 1st of every month at midnight |
| 0 0 1 1 * | January 1st at midnight (yearly) |
| 30 4 * * * | Daily at 4:30 AM |
| 0 */2 * * * | Every 2 hours |
| 0 9-17 * * 1-5 | Hourly 9AM-5PM, weekdays |
Translate Cron Expressions Instantly
Paste any cron expression and see a human-readable explanation.
Open Cron Translator4Advanced Scheduling Patterns
Complex schedules often combine multiple patterns. Here are some real-world examples.
| Expression | Meaning |
|---|---|
| 0 9,12,18 * * * | 9 AM, 12 PM, and 6 PM daily |
| 0 0 15,L * * | 15th and last day of month at midnight |
| 0 0 * * 1#1 | First Monday of every month |
| 0 0 * * 5L | Last Friday of every month |
| 0 0 1-7 * 1 | First Monday of month (1st-7th that is Monday) |
| */30 9-17 * * 1-5 | Every 30 min during business hours |
| 0 0 1 */3 * | Quarterly (Jan, Apr, Jul, Oct 1st) |
| 0 4 8-14 * 0 | 2nd Sunday of month at 4 AM |
When both day-of-month and day-of-week are specified, most cron implementations use OR logic (runs if either matches). Use ? to explicitly ignore one field in systems that support it.
5Working with crontab
The crontab command manages your cron schedules on Unix/Linux systems.
# Edit your crontab
crontab -e
# List current crontab entries
crontab -l
# Remove all crontab entries (use with caution!)
crontab -r
# Edit another user's crontab (requires root)
sudo crontab -u username -e# Example crontab file content
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
# Backup database every night at 2 AM
0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
# Clear temp files every Sunday at 4 AM
0 4 * * 0 rm -rf /tmp/cache/*
# Health check every 5 minutes
*/5 * * * * curl -s https://example.com/health > /dev/nullAlways redirect output (>> logfile 2>&1) to prevent cron from sending emails for every run. Set MAILTO="" to disable emails entirely.
6Debugging Cron Jobs
Cron jobs fail silently if not properly configured. Here's how to troubleshoot.
- 1Check cron logs: /var/log/cron or /var/log/syslog (grep CRON)
- 2Use absolute paths: Cron has minimal PATH. Use /usr/bin/python not python.
- 3Redirect output: >> /path/to/log.log 2>&1 captures stdout and stderr.
- 4Test command manually: Run the exact command in terminal first.
- 5Check permissions: Script must be executable (chmod +x script.sh).
- 6Verify cron is running: systemctl status cron or service cron status.
- 7Environment variables: Cron has minimal env. Source profiles if needed.
# Debug wrapper for cron jobs
#!/bin/bash
# Save as /home/user/scripts/cron-wrapper.sh
# Log start time
echo "=== Job started at $(date) ==="
# Load environment
source /home/user/.bashrc
# Run actual command
/home/user/scripts/my-job.sh
# Log completion
echo "=== Job completed at $(date) with exit code $? ==="Common gotcha: Scripts work in terminal but fail in cron. This is almost always a PATH or environment issue. Add "env > /tmp/cronenv.txt" to your cron job to see what environment cron provides.
7Beyond Cron: Modern Alternatives
While cron is universal, modern systems offer alternatives with additional features.
| Tool | Best For | Key Features |
|---|---|---|
| systemd timers | Linux servers | Better logging, dependency handling |
| AWS EventBridge | Cloud workloads | Serverless, managed, event-driven |
| Kubernetes CronJobs | Container workloads | Pod-based, retries, history |
| Celery Beat | Python apps | Database-backed, dynamic schedules |
| GitHub Actions | CI/CD workflows | schedule trigger with cron syntax |
| node-cron | Node.js apps | In-process, second precision |
# GitHub Actions scheduled workflow example
name: Daily Backup
on:
schedule:
# Runs at 2 AM UTC every day
- cron: '0 2 * * *'
jobs:
backup:
runs-on: ubuntu-latest
steps:
- name: Run backup
run: echo "Backup complete"8Cron Best Practices
Follow these guidelines to create reliable, maintainable cron jobs.
- Use comments: # Daily backup - added 2024-01-15 by John
- Log everything: Redirect output to timestamped log files
- Use lock files: Prevent overlapping runs with flock or similar
- Set timeouts: Kill jobs that run too long to prevent pile-ups
- Monitor failures: Alert on exit codes != 0 or missing logs
- Stagger timing: Avoid all jobs at :00 to reduce load spikes
- Document schedules: Maintain a wiki/doc of all scheduled jobs
- Test in staging: Verify cron jobs work before production
# Cron job with best practices
# Runs every hour, prevents overlapping, logs with timestamps
0 * * * * /usr/bin/flock -n /tmp/myjob.lock \
/home/user/scripts/myjob.sh >> /var/log/myjob/$(date +\%Y-\%m-\%d).log 2>&1Use flock -n (non-blocking) to skip a run if the previous one is still going, or flock (blocking) to queue runs. This prevents the "piling up" problem.
Frequently Asked Questions
What does * mean in a cron expression?
The asterisk (*) means ’every’ or ’any value.’ For example, * in the hour field means every hour. * in the day field means every day. The expression * * * * * runs every minute because all five fields are wildcards.
How do I run a cron job every 5 minutes?
Use */5 in the minute field: */5 * * * * command. The / operator creates steps. */5 means 0, 5, 10, 15, ... 55. For every 10 minutes use */10, for every 15 minutes use */15.
Why isn’t my cron job running?
Common causes: 1) PATH issues - use absolute paths like /usr/bin/python. 2) Permission denied - make script executable with chmod +x. 3) Wrong user - check which user’s crontab the job is in. 4) Cron service stopped - run systemctl status cron. 5) No output capture - add >> /tmp/log.txt 2>&1 to see errors.
What’s the difference between cron and crontab?
Cron is the daemon (background service) that runs scheduled jobs. Crontab (cron table) is the file containing the schedule entries, and also the command used to edit that file. You use ’crontab -e’ to edit your crontab file, which cron reads to know what jobs to run.
Can I run cron jobs at second precision?
Standard cron only supports minute precision. For second-level scheduling, use alternatives: sleep workarounds (run every minute, loop with sleeps), systemd timers (support seconds), or application-level schedulers like node-cron or Celery Beat that support seconds.