Cron Expressions Demystified: Schedule Anything in 5 Minutes
Cron is the backbone of scheduled automation on Unix systems. Whether you are rotating logs, triggering backups, or running a daily data pipeline, cron expressions are the language you use to describe "when." Despite being only five fields wide, the syntax trips up even experienced engineers. This guide gets you fluent in five minutes.
The 5-field format
A standard cron expression has five fields separated by spaces:
┌───────── 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 a number, a wildcard, or a pattern. The expression fires when all five fields match the current time simultaneously.
Special characters
Asterisk * -- any value
Matches every possible value for that field.
* * * * * # Every minute of every hour of every day
Comma , -- list of values
0 9,17 * * * # At 9:00 AM and 5:00 PM every day
Hyphen - -- range
0 9-17 * * * # Every hour from 9:00 AM to 5:00 PM
Slash / -- step
*/5 * * * * # Every 5 minutes
0 */2 * * * # Every 2 hours, on the hour
The slash means "starting from the beginning of the range, fire every N units." */5 in the minute field is equivalent to 0,5,10,15,20,25,30,35,40,45,50,55.
Combining characters
You can combine these within a single field:
0 9-17/2 * * 1-5 # Every 2 hours between 9 AM and 5 PM, weekdays only
Common patterns every developer needs
Here are the scheduling patterns that cover 90% of real-world use cases:
# Every minute
* * * * *
# Every 5 minutes
*/5 * * * *
# Every hour at minute 0
0 * * * *
# Daily at 3:00 AM
0 3 * * *
# Daily at midnight
0 0 * * *
# Weekdays at 9:00 AM
0 9 * * 1-5
# Every Monday at 8:30 AM
30 8 * * 1
# First day of every month at midnight
0 0 1 * *
# Every 15 minutes during business hours on weekdays
*/15 9-17 * * 1-5
# Twice a day (8 AM and 8 PM)
0 8,20 * * *
# Every Sunday at 2:00 AM (maintenance window)
0 2 * * 0
# Quarterly (first day of Jan, Apr, Jul, Oct)
0 0 1 1,4,7,10 *
The day-of-month vs day-of-week gotcha
When both day-of-month and day-of-week are specified (not *), most cron implementations treat them as an OR condition, not AND. This is counterintuitive.
0 0 15 * 5 # Fires on the 15th of every month AND every Friday
If you want "the third Friday of the month," standard cron cannot express that directly. You need a wrapper script that checks the date:
0 0 15-21 * 5 # Every Friday between the 15th and 21st
This is one of the most common sources of cron scheduling bugs.
Setting up cron jobs
Using crontab
The crontab command manages per-user cron tables:
# Edit your crontab
crontab -e
# List current jobs
crontab -l
# Remove all jobs (careful!)
crontab -r
A crontab entry includes the expression followed by the command:
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Key tips for crontab entries:
- Always use absolute paths. Cron runs with a minimal
PATH./usr/local/bin/nodeinstead of justnode. - Redirect output. Without redirection, cron sends output as email (which usually goes nowhere on modern systems). Use
>> logfile 2>&1. - Set environment variables at the top. You can define variables like
PATHandSHELLat the top of the crontab file.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
0 3 * * * /home/deploy/backup.sh >> /var/log/backup.log 2>&1
System-wide cron
Files in /etc/cron.d/ follow the same format but include a username field:
0 3 * * * root /usr/local/bin/system-backup.sh
The directories /etc/cron.daily/, /etc/cron.hourly/, /etc/cron.weekly/, and /etc/cron.monthly/ contain scripts that run at those intervals (managed by anacron or run-parts).
Crontab vs systemd timers
Modern Linux distributions offer systemd timers as an alternative to cron. Here is how they compare:
| Feature | Crontab | Systemd Timers |
|---|---|---|
| Syntax | 5-field expression | Calendar events or monotonic |
| Logging | Manual (redirect to file) | Built-in (journalctl) |
| Missed runs | Skipped silently | Persistent=true catches up |
| Dependencies | None | Can depend on other units |
| Randomized delay | Not built-in | RandomizedDelaySec |
| Per-second precision | No (minute minimum) | Yes |
A systemd timer equivalent of 0 3 *:
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
ExecStart=/usr/local/bin/backup.sh
For simple scheduling, cron is still the fastest to set up. For complex dependencies, logging, and reliability, systemd timers are worth the extra configuration.
Debugging cron jobs
When a cron job does not fire, check these things in order:
- Is the cron daemon running?
systemctl status cronorsystemctl status crond. - Is the syntax correct? A single typo silently disables the job. Use a cron expression validator.
- Check permissions. The script must be executable (
chmod +x). If using/etc/cron.d/, the file must be owned by root and not world-writable. - Check the environment. Cron does not source
.bashrcor.profile. If your script depends on environment variables, define them explicitly. - Check the logs.
grep CRON /var/log/syslogon Debian/Ubuntu orjournalctl -u cronon systemd systems.
Extended cron (6 and 7 fields)
Some systems (like Quartz for Java, or node-cron) support a seconds field at the beginning:
*/30 * * * * * # Every 30 seconds (6-field)
Spring and Quartz also add a year field at the end. These are not standard Unix cron -- always check which format your platform expects.
Cron in containers and cloud
In Docker, avoid running cron inside application containers. Instead, use the orchestrator's scheduling:
- Kubernetes: CronJob resources
- AWS: EventBridge Scheduler or CloudWatch Events
- GitHub Actions:
scheduletrigger with cron syntax - Vercel/Netlify: Scheduled functions (cron syntax)
These platforms use standard 5-field cron syntax, so everything in this guide applies.
Try our Cron Builder to visually construct and validate cron expressions -- see the next execution times instantly, right in your browser.