Cron Expressions:Syntax, Fields & Scheduling

Cron expressions schedule every automated task on the internet. Learn the five-field syntax, avoid the most common mistakes, and use cron confidently in GitHub Actions, Kubernetes, and system crontab.

Cron: The Scheduler That Runs the Internet

That database backup that runs every night at 2 AM? Cron. The SSL certificate renewal that checks weekly for expiring certs? Cron. The cache warming that rebuilds your homepage every 15 minutes? Cron. The log rotation that compresses yesterday's access logs at midnight? Cron. Every automated task that runs on a schedule — from sending weekly email reports to cleaning up temporary files — is likely powered by a cron expression. These five-field strings have been the standard for job scheduling since Unix V7 in 1979, and they're more relevant than ever: GitHub Actions uses them, Kubernetes CronJobs use them, AWS CloudWatch Events use them, and virtually every CI/CD pipeline uses them.

A cron expression generator lets you build these expressions visually. Select "every Monday at 9 AM" from dropdowns, and it outputs 0 9 * * 1. This eliminates memorization of field order and catches off-by-one errors — the most common cron mistake — before your job runs at the wrong time (or doesn't run at all).

The Five Fields, Explained

A cron expression consists of five space-separated fields, read left to right. If you remember one thing, remember the order: minute hour day-of-month month day-of-week.

Minute (0-59): The exact minute the job runs. 0 means at the top of the hour. 30 means at half past. */15 means every 15 minutes (0, 15, 30, 45). This is the most commonly customized field — most jobs don't need to run every minute, and most don't need to run at exactly the top of the hour. Distributing job start times (e.g., 0, 7, 23, 41) reduces load spikes on shared servers.

Hour (0-23): The hour in 24-hour format. 0 is midnight. 9 is 9 AM. 14 is 2 PM. */2 means every two hours (0, 2, 4, 6, ...). Be careful with hour values and timezones: a cron job set to run at 2 AM UTC runs at 7 PM Pacific the previous day. Always document which timezone your cron jobs assume.

Day of Month (1-31): The day of the month the job runs. 1 is the first of the month. 15 is the 15th. L is the last day (non-standard, supported by some implementations). This field is tricky: what happens when you specify the 31st in a month with 30 days? The job simply doesn't run that month. What about February 30th? Same — it skips. If you need "the last day of every month," use a wrapper script that checks the date rather than relying on cron alone.

Month (1-12): The month number. 1 is January, 12 is December. Most cron expressions use * (every month). This field is less commonly customized — most scheduled tasks run year-round.

Day of Week (0-7): The day of the week, where both 0 and 7 represent Sunday. 1 is Monday. 5 is Friday. 1-5 is Monday through Friday (weekdays). 0,6 is Saturday and Sunday (weekends). The fact that 0 and 7 both mean Sunday is a historical artifact from early Unix — it exists so that both 0-6 and 1-7 ranges are valid. Always use 0 or 7 consistently to avoid confusing your future self.

Special Characters and Advanced Patterns

The asterisk (*) means "every" — every minute, every hour, every day. The forward slash (/) creates step values: */5 in the minute field means every 5 minutes (0, 5, 10, 15, ..., 55). The comma (,) lists specific values: 1,15,30 means at minutes 1, 15, and 30. The hyphen (-) creates ranges: 9-17 means from 9 to 17 (inclusive). Combining these: 0 9-17 * * 1-5 means "at the top of every hour from 9 AM to 5 PM, Monday through Friday."

The day-of-month and day-of-week fields have a special interaction. When both are set to specific values (not *), the job runs when EITHER condition matches. 0 0 1 * 1 means "at midnight on the 1st of the month AND every Monday." This catches people off guard — they expect AND behavior (must match both) but cron uses OR. If you need AND behavior, use a wrapper script that checks both conditions, or schedule two separate jobs that each check one condition and exit early if the other isn't met.

Cron in Modern Infrastructure

GitHub Actions. Uses standard cron syntax with a 5-field expression: schedule: - cron: '0 9 * * 1' runs at 9 AM UTC every Monday. GitHub Actions schedules run on UTC, and there's no timezone configuration — convert your desired local time to UTC manually. Also, GitHub Actions scheduled workflows are disabled after 60 days of repository inactivity and may be delayed during high load periods. Don't rely on GitHub Actions schedules for time-critical tasks.

Kubernetes CronJobs. Uses the same 5-field syntax: schedule: "0 2 * * *" runs at 2 AM every day. Kubernetes CronJobs also support a startingDeadlineSeconds parameter: if a job misses its scheduled time (e.g., the cluster was down), it can still run as long as it's within this deadline. This prevents missed backups and data syncs during maintenance windows. Kubernetes CronJobs run in the cluster's timezone unless overridden.

System crontab. The original. Edit with crontab -e. Each line is a cron expression followed by the command to run. The system crontab runs in the server's system timezone. Best practice: set all servers to UTC, schedule cron jobs in UTC, and let application code handle timezone conversions for display. This avoids the nightmare of debugging cron jobs that ran at different times because servers in different regions have different timezones.

Use our cron expression generator to build schedules visually without memorizing field order or special character syntax. Then use Cron Expression Parser to verify what a schedule means in plain English before deploying.