Every senior software engineer has a war story involving an unintentional midnight batch job that flooded production databases or sent thousands of duplicate invoices. Nine times out of ten, the culprit was a misunderstanding of POSIX cron semantics.
The Infamous "Day of Month vs. Day of Week" OR Trap
Standard UNIX crontab contains five whitespace-separated fields:
┌───────────── Minute (0 - 59)
│ ┌───────────── Hour (0 - 23)
│ │ ┌───────────── Day of Month (1 - 31)
│ │ │ ┌───────────── Month (1 - 12)
│ │ │ │ ┌───────────── Day of Week (0 - 6, 0 = Sunday)
│ │ │ │ │
* * * * *
In almost all programming languages, matching multiple filters requires all predicates to be satisfied (logical AND). If you want an event to fire on Friday the 13th, you might write:
0 0 13 * 5
This does NOT run only when the 13th is a Friday.
In the POSIX standard (and Vixie cron), if both the Day of Month (Field 3) and Day of Week (Field 5) are specified (neither is an asterisk *), the relationship is evaluated as a logical OR.
The expression above runs on the 13th day of the month AND ALSO on every single Friday throughout the year! If you intended to run only on Friday the 13th, traditional cron cannot express this condition natively; your application handler must check the calendar day upon invocation and exit early if it is not Friday.
Step Values (*/n) vs. Specific Offsets
Another frequent source of operational surprise is the behavior of step values. Consider:
*/10 * * * *
This runs at minutes 0, 10, 20, 30, 40, and 50. But consider an hour step:
0 */5 * * *
Since hours range from 0 to 23 (24 total hours), the step values are 0, 5, 10, 15, and 20. When midnight arrives, the cycle resets to 0. This means the gap between 20:00 and 00:00 is only 4 hours, not 5!
Kubernetes CronJobs and Concurrency Policies
When moving cron schedules into Kubernetes or serverless cloud schedulers, you must configure concurrency controls. By default, if a database maintenance job scheduled for every 10 minutes takes 12 minutes to run, Kubernetes will spawn a second pod concurrently.
Always explicitly declare concurrency policy in your manifests:
spec:
schedule: "0 */2 * * *"
concurrencyPolicy: Forbid # Prevents overlapping job runs
startingDeadlineSeconds: 120
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
Using an interactive visual builder with real-time forward timestamp calculation eliminates guesswork before deploying production crontabs.