What Is Cron and Why Every Developer Needs to Understand It
Cron is a Unix time-based job scheduling daemon that has been a foundational piece of server infrastructure since 1975. The name comes from the Greek word 'chronos' (time). At its core, cron is simple: a background process that wakes up every minute, reads a list of scheduled jobs, and executes any whose time matches the current minute. Despite its age, cron remains the default scheduling mechanism for millions of Linux servers, and its expression format has been adopted by modern platforms including GitHub Actions, AWS EventBridge, Kubernetes CronJobs, Google Cloud Scheduler, and countless application frameworks. Understanding cron expressions is a fundamental DevOps and backend development skill.
Cron Expression Syntax Deep Dive
The standard cron expression has 5 space-separated fields: [minute 0-59] [hour 0-23] [day-of-month 1-31] [month 1-12] [day-of-week 0-7]. The fields support four types of values: exact values (5), ranges (1-5), lists (1,3,5), and steps (*/5). These can be combined: 0,30 in the minute field means 'at minutes 0 and 30'. 1-5 in day-of-week means 'Monday through Friday'. */15 in the minute field means 'every 15 minutes'. The wildcard (*) means 'any value for this field'. The critical insight many beginners miss: the expression fires when ALL fields simultaneously match the current time. So 0 9 1 * 1 doesn't mean 'at 9am on the 1st OR on Mondays' — in most implementations, it means 'at 9am on the 1st of the month OR at 9am on any Monday'. The day-of-month and day-of-week fields have OR semantics when both are non-wildcard — this is a common source of scheduling bugs.
Platform Differences: Linux vs GitHub Actions vs AWS vs Quartz
Not all cron is the same. Linux/Unix crontab uses the original 5-field format, runs in the system's local timezone, and fires immediately when the time matches. GitHub Actions uses the same 5-field format but always runs in UTC — this trips up developers on every non-UTC timezone. AWS EventBridge uses a 6-field format with a Year field and requires ? in either dom or dow. Quartz Scheduler (Java/Spring) uses 6 or 7 fields, adds a Seconds field at the start, uses 1-7 numbering for day-of-week (1=Sunday), and supports advanced L/W/# operators not available in standard cron. Kubernetes CronJob uses standard 5-field format but runs in UTC by default (timezone support added in k8s 1.27). When copying a cron expression between platforms, always verify the field count and timezone assumptions match.
The Most Common Cron Scheduling Patterns
These patterns cover 90% of real-world cron use cases. Every minute: * * * * *. Every N minutes: */N * * * * (e.g., */5 for every 5 minutes). Every hour: 0 * * * *. Every hour at a specific minute: M * * * * (e.g., 30 * * * * for half-past every hour). Daily at a specific time: M H * * * (e.g., 0 2 * * * for 2am daily). Weekdays at a specific time: M H * * 1-5. Weekly: M H * * D (e.g., 0 0 * * 0 for Sunday midnight). Monthly: M H D * * (e.g., 0 0 1 * * for midnight on the 1st). The most important rule: always specify the minute and hour fields explicitly when you want a specific time. The expression 0 2 * * * ('at minute 0, hour 2, every day') is unambiguous. The expression * 2 * * * ('every minute of hour 2') runs 60 times in that hour — a common mistake when someone means 'at 2am'.
Cron Best Practices for Production Systems
Production cron jobs need more care than development scripts. Always redirect output: append >> /var/log/myjob.log 2>&1 to every crontab entry — without it, failed jobs produce no trace. Use full paths for commands: cron's PATH is minimal, so /usr/local/bin/node instead of just node. Add a lock mechanism for long-running jobs: use flock -n /tmp/myjob.lock your_command to prevent overlap if the previous run is still executing. Set reasonable timeouts: add timeout 300 your_command to kill runaway jobs after 5 minutes. Use MAILTO=your@email.com at the top of crontab to receive error emails. For critical jobs, add monitoring: ping a dead-man's switch service after each successful run to alert when a job doesn't fire. Avoid too-frequent schedules: */1 (every minute) is easy to set but can cause thundering-herd problems if the job ever hangs — use */5 unless you genuinely need minute-level frequency.
Alternatives to Cron for Modern Applications
While cron remains ubiquitous, modern applications often benefit from dedicated scheduling infrastructure. Managed schedulers: Google Cloud Scheduler, AWS EventBridge, and Azure Logic Apps offer reliability guarantees, retry logic, and monitoring that raw cron lacks. Application-level schedulers: Node.js (node-cron, Agenda, Bull), Python (Celery Beat, APScheduler, Rq-scheduler), Ruby (Sidekiq-cron, Clockwork), and Java (Quartz, Spring @Scheduled) integrate scheduling directly into your application with job history, retry, and distributed coordination. Workflow orchestrators: Apache Airflow, Temporal, Prefect, and Dagster handle complex multi-step scheduled workflows with dependencies, retries, and observability that cron cannot provide. When to stick with cron: simple server-level tasks (backups, log rotation, cleanup scripts), lightweight monitoring jobs, and scripts that don't require retry logic or coordination. When to move beyond cron: jobs that must not overlap, jobs requiring retry on failure, jobs with complex dependencies, or jobs that need centralized monitoring across multiple servers.