One misplaced asterisk in a crontab file turns a nightly database backup (0 2 * * *) into a job that spawns once every sixty seconds between 2:00 AM and 2:59 AM (* 2 * * *), saturating disk I/O before anyone wakes up. Reading and writing 5-field cron expressions accurately comes down to memorizing the five positional slots and understanding two non-obvious quirks in how Linux cron evaluates step values and day fields.
Anatomy of a 5-Field Cron Expression (* * * * *)
A standard POSIX/Linux crontab schedule consists of five whitespace-separated time fields followed by the shell command to execute:
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23, 24-hour clock)
│ │ ┌───────────── day of the month (1 - 31)
│ │ │ ┌───────────── month (1 - 12, or JAN - DEC)
│ │ │ │ ┌───────────── day of the week (0 - 6, Sunday = 0 or 7)
│ │ │ │ │
* * * * * /usr/local/bin/backup.shOperator Rules: * matches every value in the field. , separates a list (1,15). - defines an inclusive range (1-5 for Monday through Friday). / defines a step increment across the range (*/5 in the minute slot runs at 0, 5, 10, ..., 55).
16 Copy-Paste Cron Expression Examples
| Schedule Description | 5-Field Cron Expression | Exact Trigger Times |
|---|---|---|
| Every minute | * * * * * | Every 60 seconds |
| Every 5 minutes | */5 * * * * | :00, :05, :10, ..., :55 of every hour |
| Every 15 minutes | */15 * * * * | :00, :15, :30, :45 of every hour |
| Every 30 minutes | */30 * * * * | :00 and :30 of every hour |
| Top of every hour | 0 * * * * | 00:00, 01:00, 02:00, ... |
| Every 6 hours | 0 */6 * * * | 00:00, 06:00, 12:00, 18:00 |
| Every day at midnight | 0 0 * * * | 00:00 daily |
| Every day at 2:30 AM | 30 2 * * * | 02:30 daily |
| Weekdays at 9:00 AM | 0 9 * * 1-5 | 09:00 Monday through Friday |
| Weekdays at 9:00 AM and 5:00 PM | 0 9,17 * * 1-5 | 09:00 and 17:00 Monday through Friday |
| Every Sunday at 3:15 AM | 15 3 * * 0 | 03:15 on Sundays |
| 1st of every month at midnight | 0 0 1 * * | 00:00 on day 1 of each month |
| 1st and 15th of the month at 6:00 AM | 0 6 1,15 * * | 06:00 on the 1st and 15th |
| First day of every quarter (Jan, Apr, Jul, Oct) | 0 0 1 */3 * | 00:00 on Jan 1, Apr 1, Jul 1, Oct 1 |
| Every 10 minutes during business hours | */10 9-17 * * 1-5 | Every 10 min from 09:00 to 17:50, Mon-Fri |
| Once a year on January 1st at midnight | 0 0 1 1 * | 00:00 on Jan 1 |
Before pasting a new schedule into /etc/crontab, GitHub Actions, or Kubernetes CronJob manifests, test the expression and inspect its next firing times in the interactive generator.
Three Crontab Pitfalls That Break Production Jobs
1. The Day-of-Month vs. Day-of-Week OR Trap
Developers frequently write 0 9 15 * 1 expecting the job to run only when the 15th of the month falls on a Monday. Standard cron evaluates field 3 (Day of Month) and field 5 (Day of Week) with a logical OR whenever both are restricted. That schedule runs on the 15th of every month and on every single Monday.
To restrict a job to "Friday the 13th" or "the first Monday of the month", put the day range in cron and test the weekday inside the shell command:
# Run at 09:00 on the first Monday of every month (days 1 through 7, weekday = Monday)
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /opt/scripts/monthly-report.shNote the backslash in +\%u: inside a crontab file, an unescaped % character is interpreted by cron as a newline that passes everything after it to stdin. Always escape percent signs as \% in crontab lines.
2. Server UTC vs. Local Daylight Saving Time
My recommendation for production servers and container clusters is to run cron daemons in UTC year-round. Only schedule in a local timezone with Daylight Saving Time (such as America/New_York or Europe/London) if the job sends a customer-facing notification at a fixed local wall-clock hour.
When a server uses local DST, a job scheduled for 02:30 (30 2 * * *) is skipped entirely on the spring-forward morning and may run twice on the fall-back morning. Convert your target local business hour to UTC first so you know the exact hour slot.
3. Auditing Execution Logs with Unix Timestamps
When debugging overlapping cron runs (flock -n /tmp/job.lock) or comparing syslog entries against database created_at epochs, convert the recorded Unix epoch seconds directly to UTC and local ISO-8601 strings.