Cron Expressions: The Complete Guide with Examples

March 12, 2025 · 11 min read · By Michael Lip

Cron is one of the oldest and most reliable scheduling systems in computing. Originally written by Ken Thompson for Unix in 1975, the cron daemon has been running scheduled tasks on servers for fifty years. Despite the rise of modern scheduling services (AWS EventBridge, Google Cloud Scheduler, Kubernetes CronJobs), the cron expression syntax remains the universal language for describing recurring schedules.

This guide covers the complete cron syntax, walks through real-world scheduling examples, and identifies the mistakes that catch even experienced DevOps engineers.

The Five Fields

A standard cron expression has five fields, separated by spaces:

  *     *     *     *     *
  |     |     |     |     |
  |     |     |     |     +-- Day of Week (0-6, Sunday=0)
  |     |     |     +-------- Month (1-12)
  |     |     +-------------- Day of Month (1-31)
  |     +-------------------- Hour (0-23)
  +-------------------------- Minute (0-59)

Each field accepts four types of values:

Common Patterns and Examples

Basic Schedules

# Every minute
* * * * *

# Every 5 minutes
*/5 * * * *

# Every hour (at minute 0)
0 * * * *

# Daily at midnight
0 0 * * *

# Daily at 2:30 AM
30 2 * * *

# Weekly on Sunday at midnight
0 0 * * 0

# Monthly on the 1st at midnight
0 0 1 * *

# Yearly on January 1st at midnight
0 0 1 1 *

Business Hours Schedules

# Weekdays at 9 AM
0 9 * * 1-5

# Every 30 minutes during business hours on weekdays
*/30 9-17 * * 1-5

# Monday and Friday at 8 AM
0 8 * * 1,5

# Weekdays at 9 AM and 5 PM
0 9,17 * * 1-5

Operational Schedules

# Database backup: daily at 3 AM
0 3 * * *

# Log rotation: every 6 hours
0 */6 * * *

# Health check: every 2 minutes
*/2 * * * *

# SSL certificate renewal check: 1st and 15th of each month at 4 AM
0 4 1,15 * *

# Quarterly report: 1st day of Jan, Apr, Jul, Oct at 6 AM
0 6 1 1,4,7,10 *

You can test any of these expressions with our cron parser to see the next execution times.

Understanding Step Values

The step operator (/) is the most misunderstood part of cron syntax. */n means "every nth value starting from the minimum." But m/n means "every nth value starting from m."

# Every 15 minutes starting from minute 0: 0, 15, 30, 45
*/15 * * * *

# Every 15 minutes starting from minute 5: 5, 20, 35, 50
5/15 * * * *

# Every 3 hours starting from hour 2: 2, 5, 8, 11, 14, 17, 20, 23
0 2/3 * * *

A common mistake is writing */7 * * * * expecting it to run every 7 minutes evenly. It runs at minutes 0, 7, 14, 21, 28, 35, 42, 49, 56 — and then at minute 0 again, only 4 minutes after the last run. The step resets each hour because the minute field has a range of 0-59.

Day of Month vs Day of Week

The interaction between field 3 (day of month) and field 5 (day of week) is the most confusing aspect of cron. In traditional Unix cron, if both are specified (not *), the job runs when either condition is true — it is an OR, not an AND.

# Runs on the 15th of each month AND every Friday
0 0 15 * 5

# This does NOT mean "the 15th if it falls on Friday"
# It means "every 15th" OR "every Friday"

This behavior surprises almost everyone. Some cron implementations (like Quartz in Java) change this to AND logic, but standard Unix cron uses OR. Always check which implementation you are using.

Cron in Modern Infrastructure

Kubernetes CronJobs

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-cleanup
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cleanup
            image: my-cleanup:latest
          restartPolicy: OnFailure

Kubernetes CronJobs use standard 5-field cron syntax. The concurrencyPolicy field is critical — Forbid prevents a new job from starting if the previous one is still running. Without this, long-running jobs can pile up.

GitHub Actions

name: Nightly Tests
on:
  schedule:
    - cron: '0 2 * * *'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

GitHub Actions uses UTC for all cron schedules. There is no way to specify a timezone. If you need a job to run at "9 AM Pacific," you calculate the UTC equivalent (which changes with DST: 17:00 UTC in winter, 16:00 UTC in summer).

AWS EventBridge (CloudWatch Events)

# AWS uses a 6-field format (adds year) and different day-of-week values
# Minutes Hours DayOfMonth Month DayOfWeek Year
cron(0 3 * * ? *)

# Note: AWS uses ? for "no specific value" in day fields
# Day of week: SUN=1 (not 0 like Unix)

AWS deviates from standard cron in several ways: six fields instead of five, day-of-week starting at 1 (SUN) instead of 0, and the ? wildcard for the day-of-month/day-of-week ambiguity. Always check the documentation for your specific platform.

Debugging Cron Jobs

When a cron job fails silently (which they often do), here is a systematic debugging approach:

  1. Check the expression — Use a cron parser to verify the next execution times match your expectations.
  2. Check the timezone — The cron daemon runs in the system timezone by default. If the server is UTC but you expected local time, your job runs at the wrong hour.
  3. Check the PATH — Cron runs with a minimal environment. Commands that work in your shell may fail in cron because the PATH is different. Use absolute paths.
  4. Check output capture — By default, cron sends output (stdout and stderr) via email. If email is not configured, output is silently discarded. Redirect to a log file: 0 3 * * * /usr/bin/my-script >> /var/log/my-script.log 2>&1
  5. Check permissions — The cron daemon runs your job as your user, but the environment differs from an interactive session. Files that need specific permissions or environment variables may fail.

Cron Alternatives

While cron is reliable for simple periodic schedules, it has limitations:

For machine learning workflows specifically, cron is often used for model retraining schedules and data pipeline orchestration. Platforms discussed on LockML and developer tools handle these schedules at scale, but the underlying scheduling language remains cron expressions.

Quick Reference

Field        Range         Special Characters
Minute       0-59          * , - /
Hour         0-23          * , - /
Day of Month 1-31          * , - /
Month        1-12          * , - /
Day of Week  0-6 (Sun=0)   * , - /

10 Common Expressions:
* * * * *       Every minute
*/5 * * * *     Every 5 minutes
0 * * * *       Hourly
0 0 * * *       Daily midnight
30 2 * * *      Daily 2:30 AM
0 9 * * 1-5     Weekdays 9 AM
0 0 * * 0       Weekly Sunday
0 0 1 * *       Monthly 1st
0 0 1 1 *       Yearly Jan 1
0 */6 * * *     Every 6 hours

Conclusion

Cron expressions are a compact, powerful scheduling language that has survived for fifty years because it solves a real problem well. The five-field syntax — minute, hour, day of month, month, day of week — covers the vast majority of scheduling needs. The gotchas (step value resets, OR logic for day fields, timezone confusion, PATH issues) are well-documented and avoidable with awareness.

Master cron expressions once, and you have a skill that works everywhere: Linux servers, Kubernetes, CI/CD pipelines, cloud schedulers, and monitoring systems. It is one of those rare pieces of technology where the time investment pays dividends for an entire career.