New to Claude Skills? Learn how to install them →

sickn33 on GitHub

Cron Doctor

Free

Validate and debug your cron expressions with ease.

Get this skill

Free · Opens the source repo

What Cron Doctor does

Cron expressions are often deceptively simple, yet they can lead to significant issues if not validated properly. The Cron Doctor skill is designed to diagnose and validate cron expressions before they are deployed, helping developers avoid silent failures that can disrupt scheduled tasks. This skill identifies five common pitfalls—impossible dates, OR-semantics, midnight spikes, uneven step drift, and leap-year anomalies—that can cause cron jobs to behave unexpectedly. By catching these issues early, it ensures that your scheduled tasks run as intended without surprises.

The skill includes a zero-dependency validation engine that can be used programmatically or via a command-line interface. This makes it easy to integrate into existing workflows or use as a standalone tool. The validation process involves parsing the cron expression, describing its intended behavior in plain English, checking for the common pitfalls, and calculating the next execution times. This comprehensive approach provides users with a clear understanding of how their cron jobs will function.

Whether you're writing, editing, or reviewing cron expressions for crontabs, Kubernetes CronJobs, or CI/CD pipelines, Cron Doctor is a valuable tool for ensuring accuracy and reliability. It is particularly useful when debugging jobs that fail to execute or when users need clarification on the behavior of a cron expression. By providing insights into potential issues and offering suggestions for fixes, it empowers developers to write more robust scheduling configurations.

In summary, Cron Doctor is an essential skill for anyone working with cron jobs, whether in development, operations, or automation contexts. It helps prevent costly mistakes and enhances the reliability of scheduled tasks in production environments.

When to use it

Use this skill when creating or reviewing cron expressions to ensure they function as intended.

When not to use it

This skill may not be necessary for users who do not work with cron jobs or scheduled tasks regularly.

What you can build with it

Validating a Kubernetes CronJob

When defining a CronJob in Kubernetes, use Cron Doctor to ensure the cron expression is valid and won't lead to silent failures.

Debugging a Failed Cron Job

If a scheduled job isn't firing as expected, run the cron expression through Cron Doctor to identify any potential misconfigurations.

Reviewing CI/CD Schedules

Before deploying a CI/CD pipeline with scheduled tasks, use Cron Doctor to validate all cron expressions for correctness.

How to install Cron Doctor

View source

1. Install with the skills CLI

npx skills add sickn33/agentic-awesome-skills/cron-doctor --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by sickn33

cron-doctor

Overview

Cron is deceptively error-prone. The failure mode is silent — a syntactically valid expression that simply never fires, or fires far more often than intended. 0 0 30 2 * parses cleanly and then sits dead forever (February has no 30th). 0 0 1,15 * 1 looks like "1st and 15th if Monday" but actually means "1st, 15th, OR every Monday" — ~6 fires/month instead of ~2.

This skill teaches an agent to catch those before they reach production. It comes with a zero-dependency validation engine (scripts/cron-engine.js, no install needed) that parses, describes, deep-validates, and computes next fire times.

When to Use This Skill

  • Use when a user writes, edits, reviews, or deploys a cron expression — in a crontab, a Kubernetes CronJob, a GitHub Actions schedule, an Airflow DAG, a Celery beat schedule, a systemd timer, or any scheduled task.
  • Use when debugging a job that "didn't fire" or "fired at the wrong time."
  • Use when a user asks "what does this cron expression mean?" or "when will this run next?" or "how often does this run per year?"
  • Use when reviewing a CI/CD pipeline or infrastructure config that contains a schedule field.
  • Use when a user pastes a 5-field cron expression and asks for a sanity check.

How It Works

Step 1: Parse the expression

Split on whitespace into 5 fields: minute, hour, day-of-month, month, day-of-week. Confirm valid ranges:

FieldPositionRangeNotes
minute10–59
hour20–23
day-of-month31–31
month41–12names (JAN–DEC) accepted
day-of-week50–70 and 7 both = Sunday; names (SUN–SAT) accepted

Step 2: Describe it in plain English

State what the user thinks it does vs. what it actually does. Be explicit about OR-vs-AND semantics for day-of-month + day-of-week (see death-trap #2).

Step 3: Run the trap checklist

Check the five death-traps below and flag any that apply.

Step 4: Calculate next runs and annual fire count

Compute the next 5 fire times as concrete dates so the user can verify the schedule behaves as expected. Estimate annual fire count — a schedule that fires 365×/year vs. 12×/year is a ~30× cost and load difference.

The Five Cron Death-Traps

These are the bugs that pass crontab -l validation but break in production.

1. Impossible dates — the "never fires" bug

0 0 30 2 *

Valid syntax. Never fires. February has no 30th. This schedule is a dead job that silently sits forever. The same applies to day 31 in any 30-day month: 0 0 31 4 *, 0 0 31 6 *, 0 0 31 9 *, 0 0 31 11 *.

Fix: use 0 0 28-31 * * and check for end-of-month in the script, or use L (last day) syntax if your scheduler supports it.

2. OR-semantics — the "fires too often" bug

0 0 1,15 * 1

Does NOT mean "midnight on the 1st and 15th if it's Monday." Does mean "midnight on the 1st, the 15th, OR every Monday." That's ~6 fires/month instead of ~2.

This is the single most misunderstood cron rule. When both day-of-month AND day-of-week are restricted (neither is *), cron uses OR logic, not AND.

Fix: if you need "1st and 15th only if Monday," run daily and check in the script:

0 0 * * 1 [ "$(date +%d)" = "01" -o "$(date +%d)" = "15" ] && your-command

3. Midnight spike — the "everything at once" bug

0 0 * * *

Every job scheduled at 0 0 competes for resources at exactly 00:00. Database backups, log rotations, cert renewals, report generation — all fire simultaneously. This causes load spikes, connection-pool exhaustion, and cascading timeouts.

Fix: stagger jobs across the hour. Use 17 2 * * * or 43 3 * * * instead of 0 0. Jitter is your friend.

4. Uneven steps — the "drift" bug

*/7 * * * *

Does NOT mean "every 7 minutes evenly." It means "every 7 minutes starting at 0, then resets at 60." So: 0, 7, 14, 21, 28, 35, 42, 49, 56 — then 0 again (a 4-minute gap). The intervals drift: 7,7,7,7,7,7,7,7,4.

Fix: 60 is not divisible by 7. Use step values that divide 60 evenly: */5, */10, */15, */20, */30. If you truly need every-7-minutes, use a loop with sleep 420.

5. Leap-year February 29 — the "annual surprise"

0 0 29 2 *

Fires only on leap years — February 29, 2024 / 2028 / 2032… If someone writes this expecting "end of February," they'll be confused for 3 out of every 4 years.

Fix: use 0 0 28 2 * and handle the 29th case in the script if needed.

Using the validation script

This skill ships a zero-dependency engine at scripts/cron-engine.js (Node.js, no npm install needed). You can use it programmatically or from the CLI:

// Programmatic — Node.js, zero dependencies
const { describe, validate, nextRuns, formatNextRuns } = require('./scripts/cron-engine.js');

// Parse + describe -> returns { text, error, parsed }
const d = describe('0 0 30 2 *');
console.log(d.text);   // "At 00:00, on day-of-month 30 in in FEB"

// Deep validation -> catches the traps
const result = validate('0 0 30 2 *');
console.log(result.valid);              // true (syntax is valid)
console.log(result.observations);       // includes the "never fires" insight
console.log(result.suggestions);        // e.g. "Midnight is a common spike..."

// Next 5 fire times -> returns Date[]
const runs = nextRuns('0 9 * * 1-5', new Date(), 5);
console.log(formatNextRuns(runs, new Date())); // [{ date, relative, formatted }, ...]
# CLI (via the bundled wrapper)
node scripts/cli.js describe "*/5 * * * *"
node scripts/cli.js validate "0 0 30 2 *"
node scripts/cli.js next "0 9 * * 1-5" 5

Common cron presets

ExpressionDescriptionUse case
*/5 * * * *Every 5 minutesHealth checks, polling
0 * * * *Every hourHourly aggregation
0 */2 * * *Every 2 hoursSemi-frequent sync
0 9 * * 1-59am Mon–FriBusiness-hours task
0 2 * * *2am dailyOff-peak batch (avoid midnight)
0 0 * * 0Midnight SundayWeekly maintenance
0 0 1 * *Midnight 1st of monthMonthly report
0 0 1 1 *Midnight Jan 1stAnnual task

Best Practices

  • ✅ Always provide the plain-English description AND run the trap checklist.
  • ✅ Stagger midnight jobs to avoid the spike.
  • ✅ Prefer step values that divide 60 evenly (*/5, */15, */30).
  • ✅ Add a comment above every crontab line explaining intent.
  • ✅ Set an explicit timezone (CRON_TZ) on schedulers that support it.
  • ❌ Don't trust crontab -l validation — it only checks syntax, not semantics.
  • ❌ Don't restrict both day-of-month and day-of-week without confirming OR-logic.
  • ❌ Don't schedule everything at 0 0.

Common Pitfalls

  • Problem: "My cron job isn't running." Solution: Check for an impossible date (trap #1) and confirm the daemon is running (service cron status / systemctl status crond). Verify the file ends with a newline and has correct ownership.

  • Problem: "My job runs far more often than expected." Solution: You hit OR-semantics (trap #2). If both day-of-month and day-of-week are set, cron ORs them. Move one to * or guard in-script.

  • Problem: "Intervals are uneven — sometimes 7 min, sometimes 4." Solution: Step value doesn't divide 60 evenly (trap #4). Use a divisor of 60.

  • Problem: "My job works locally but not in the cluster." Solution: Timezone mismatch. Kubernetes CronJob and GitHub Actions default to UTC. Confirm timeZone / TZ is set as intended.

Limitations

  • This skill targets standard 5-field cron as implemented by Vixie cron, systemd timers, Kubernetes CronJob, GitHub Actions schedule, and most libraries. It does not validate Quartz 6/7-field expressions with seconds/years, nor non-standard @reboot / L / # extensions without a note.
  • Estimated annual fire counts assume a non-leap reference year; February 29 schedules (trap #5) are flagged explicitly.
  • This skill does not replace environment-specific validation, testing, or expert review. Stop and ask for clarification if required inputs, permissions, or safety boundaries are missing.

Related Skills

  • docker-expert — when the cron job runs inside a container and the issue is the container/entrypoint rather than the schedule.
  • kubernetes-deployment — when validating a CronJob manifest's spec.schedule field alongside the broader resource config.

Security & Safety Notes

This skill is read-only and risk: safe. The validation script performs no file writes, network calls, or mutations — it only parses and computes. It is safe to run against any cron expression without preconditions.

Frequently asked questions about Cron Doctor

Similar skills