Automated recurring tasks form the backbone of modern backend infrastructure—from running nightly database backups and billing reconciliation sweeps to invalidating Redis caches and aggregating analytics metrics.
While Unix cron and its cloud counterparts (AWS EventBridge, Google Cloud Scheduler, Kubernetes CronJobs, and GitHub Actions) rely on compact cron expressions, subtle misunderstandings of syntax variations and Daylight Saving Time (DST) transitions cause silent job skips, double-billing race conditions, and production outages.
This production guide demystifies standard 5-field vs 6-field cron syntax, uncovers common scheduling pitfalls, and details proven patterns for reliable, timezone-aware job execution.
1. Standard 5-Field vs. 6/7-Field Syntax
Cron formats vary across operating systems, schedulers, and programming libraries. Understanding the exact format your runtime expects is the first defense against scheduling bugs.
Standard POSIX / Unix Crontab (5 Fields)
Standard Unix crontab expressions consist of 5 space-delimited fields representing:
┌───────────── Minute (0 - 59)
│ ┌─────────── Hour (0 - 23)
│ │ ┌───────── Day of Month (1 - 31)
│ │ │ ┌─────── Month (1 - 12 or JAN - DEC)
│ │ │ │ ┌───── Day of Week (0 - 6 or SUN - SAT, 0 = Sunday)
│ │ │ │ │
* * * * *
Extended 6-Field and 7-Field Schedulers (Quartz, Spring, AWS EventBridge)
Frameworks like Java Quartz, Spring @Scheduled, and AWS EventBridge introduce seconds precision or explicit year fields:
- Spring / Quartz (6 fields):
[Second] [Minute] [Hour] [Day-of-Month] [Month] [Day-of-Week] - AWS EventBridge (6 fields with
?wildcards):[Minute] [Hour] [Day-of-Month] [Month] [Day-of-Week] [Year]
Note: In AWS EventBridge and Quartz, you cannot specify both Day-of-Month and Day-of-Week simultaneously with *. You must use ? (no specific value) for one of them.
# AWS EventBridge: 10:15 AM UTC every weekday
cron(15 10 ? * MON-FRI *)
2. Cron Syntax Operators Cheat Sheet
| Operator | Character | Description | Example | Explanation |
|---|---|---|---|---|
| Wildcard | * |
Matches every possible value in that field | * * * * * |
Every minute of every day |
| Step / Interval | / |
Specifies increments from a starting value | */15 * * * * |
Every 15 minutes (:00, :15, :30, :45) |
| List / Value Enumeration | , |
Matches any value in a discrete set | 0 9,13,18 * * * |
At 09:00, 13:00, and 18:00 |
| Range | - |
Matches any inclusive range of numbers | 0 9-17 * * 1-5 |
On the hour, 9 AM to 5 PM, Monday through Friday |
| No Specific Value | ? |
Used in Quartz/AWS when Day-of-Month or Day-of-Week is unconstrained | 0 0 1 * ? |
First day of every month, regardless of weekday |
| Last Day | L |
Last day of month (L) or last Friday (5L) |
0 0 L * * |
Midnight on the last day of the month |
| Weekday | W |
Nearest weekday (Mon-Fri) to a given calendar day | 0 0 15W * * |
Midnight on the nearest weekday to the 15th |
| Nth Occurrence | # |
Specifies the Nth weekday of a month (6#3 = 3rd Friday) |
0 0 * * 5#2 |
Midnight on the 2nd Friday of each month |
Tip: Verify your exact cron schedule and preview upcoming execution timestamps instantly using the DevFlow Cron Parser.
3. The 2:00 AM Daylight Saving Time (DST) Trap
The most dangerous pitfall in production job scheduling is anchoring time-sensitive cron jobs between 01:00 AM and 03:00 AM in local timezones that observe Daylight Saving Time.
Spring Forward (March):
Time: 01:58 01:59 [02:00 -> CLOCK JUMPS TO 03:00] 03:01
Result: Cron job scheduled for 02:30 AM NEVER RUNS!
Fall Back (November):
Time: 01:58 01:59 02:00 ... 02:59 [02:00 REPEATS] 02:01 ...
Result: Cron job scheduled for 02:30 AM RUNS TWICE!
The Two Critical DST Failure Modes:
- Spring Forward (Lost Execution): When clocks advance from 01:59:59 directly to 03:00:00, the entire 2:00 AM–2:59 AM hour does not exist in local time. Any cron job configured to run at
30 2 * * *will be silently skipped. - Fall Back (Duplicate Execution): When clocks shift backward from 02:59:59 to 02:00:00, local time repeats for 60 minutes. Schedulers tracking wall-clock time will fire the 2:30 AM job a second time, risking duplicate payout processing, double reporting, or inventory sync race conditions.
Architectural Solutions for DST Resilience
- Rule 1: Always Run Infrastructure Cron in UTC. Set system daemons, Kubernetes clusters, and container runners (
TZ=UTC) to UTC. UTC does not observe Daylight Saving Time and progresses monotonically without jumps or duplicate hours. - Rule 2: Avoid the 01:00–03:00 AM Window for Local Time Jobs. If business logic mandates running in local timezone (e.g., generating daily digests at 3:30 AM local), schedule jobs after 03:00 AM when DST shifts have concluded.
- Rule 3: Enforce Idempotency via Distributed Locks. Use unique execution keys stored in Redis or database transactions (
job_name + date_key) withSETNXto guarantee a job cannot execute twice within the same calendar day.
4. Standard Non-Standard Predefined Shorthands
Most modern cron implementations (including Vixie Cron, cronie, systemd timers, and Go robfig/cron) support intuitive string macros:
| Macro | Equivalent Expression | Purpose |
|---|---|---|
@reboot |
Run once at system boot | Initialize local caches or notify monitoring services |
@yearly / @annually |
0 0 1 1 * |
Run once a year at midnight on January 1st |
@monthly |
0 0 1 * * |
Run once a month at midnight on the 1st |
@weekly |
0 0 * * 0 |
Run once a week at midnight on Sunday |
@daily / @midnight |
0 0 * * * |
Run once a day at midnight |
@hourly |
0 * * * * |
Run once an hour at the start of the hour |
5. Production Cron Debugging Checklist
When a scheduled job fails to execute as expected, follow this diagnostic checklist:
- Verify Schedulers Environment Variables: Crontab does not inherit user shell profiles (
.bashrc,.zshrc). Environment variables likePATH,NODE_ENV, andDATABASE_URLare often missing. Explicitly define paths or source environment files:0 3 * * * /usr/bin/node /app/scripts/cleanup.js >> /var/log/cleanup.log 2>&1 - Check Timezone Alignment: Validate whether the scheduler host and the application database agree on timezones. Convert between UTC, EST, and ISO-8601 timestamps using the DevFlow Timezone Converter and Timestamp Tool.
- Capture Both STDOUT and STDERR: By default, cron pipes output to the local user mailbox. Redirect all output to log files or standard logging sinks (
>> /var/log/myjob.log 2>&1). - Prevent Job Overlap: If a batch job takes longer than its schedule interval (e.g., a 10-minute job scheduled every 5 minutes), use
flockor a distributed Redis lock to prevent execution pile-ups:*/5 * * * * /usr/bin/flock -n /tmp/myjob.lock /usr/bin/python3 /app/sync.py
Frequently Asked Questions
What happens if I set both Day-of-Month and Day-of-Week in standard Unix cron?
In standard POSIX/Unix cron, if both Day-of-Month and Day-of-Week are specified (not *), the condition is evaluated as a logical OR, not AND. For example, 0 0 15 * 5 will execute on the 15th of the month and every Friday.
How do I run a cron job every 45 seconds?
Standard Unix crontab does not support seconds precision. You can achieve sub-minute scheduling by using a 6-field scheduler like Quartz/Spring (*/45 * * * * *), systemd timers with AccuracySec=1s, or a runner script with a sleep offset.
Why did my cron job fail inside a Docker container?
Containerized cron engines often terminate because standard cron daemons run as background daemons and exit immediately unless run in the foreground (cron -f), or because the container lacks standard syslog/mail utilities. Prefer native orchestrator scheduling like Kubernetes CronJob or AWS ECS Scheduled Tasks.