Cron Syntax Cheat Sheet — Every Field Explained
Your deploy pipeline ran at 3 AM instead of 3 PM. Your backup job fires every minute instead of every hour. Cron syntax is five fields of pure ambiguity — here's how to read and write it without guessing.
This guide covers what every cron field means, the special characters that modify them, 15+ ready-to-use schedule expressions, platform-specific differences across Linux, GitHub Actions, AWS, and Kubernetes, and the most common reasons a cron job silently fails. Bookmark it, or use the interactive Cron Explainer to test expressions in real time.
Cron Field Reference
A standard cron expression consists of exactly five fields separated by spaces. Each field controls one dimension of the schedule. The fields are always read left to right in this order:
| Field | Position | Allowed Values | Special Characters | Example |
|---|---|---|---|---|
| Minute | 1st | 0–59 | * , - / |
*/10 = every 10 minutes |
| Hour | 2nd | 0–23 | * , - / |
9-17 = 9 AM through 5 PM |
| Day of month | 3rd | 1–31 | * , - / ? |
1,15 = 1st and 15th |
| Month | 4th | 1–12 or JAN–DEC | * , - / |
1,4,7,10 = quarterly |
| Day of week | 5th | 0–6 (Sun=0) or SUN–SAT | * , - / ? |
1-5 = Monday through Friday |
Special character reference
*(asterisk) — matches every possible value for the field.*in the hour position means "every hour.",(comma) — defines a list.1,3,5in the day-of-week field means Monday, Wednesday, and Friday.-(hyphen) — defines a range.9-17in the hour field means every hour from 9 AM through 5 PM inclusive./(slash) — defines a step.*/5in the minute field means "starting at 0, every 5 minutes" (0, 5, 10, 15...). You can also write3/10to mean "starting at minute 3, every 10 minutes" (3, 13, 23, 33, 43, 53).?(question mark) — means "no specific value." Used in some implementations (Quartz, AWS EventBridge) in either the day-of-month or day-of-week field when the other field is already constrained. Standard Linux cron does not support?.
These characters can be combined. For example, 0-30/10 in the minute field matches minutes 0, 10, 20, and 30 — a step applied over a range.
Common Cron Schedules
Below are 15+ expressions covering the most frequently needed schedules. Copy them directly or paste them into the Cron Explainer to verify run times in your timezone.
| Expression | Schedule | Notes |
|---|---|---|
* * * * * |
Every minute | Use with caution — high frequency can overload resources |
*/5 * * * * |
Every 5 minutes | Common for health checks and lightweight polling |
0 * * * * |
Every hour (on the hour) | Minute field is 0, not * |
0 0 * * * |
Every day at midnight | Midnight in the server's configured timezone |
0 12 * * * |
Every day at noon | |
0 9 * * 1 |
Every Monday at 9 AM | 1 = Monday in standard cron (0 = Sunday) |
0 8 * * 1-5 |
Weekdays at 8 AM | 1-5 covers Monday through Friday |
0 0 1 * * |
First of every month at midnight | |
*/15 8-17 * * 1-5 |
Every 15 min during business hours | 8:00 AM through 5:45 PM, weekdays only |
0 9,17 * * * |
Twice a day (9 AM and 5 PM) | Comma separates the two hours |
0 2 * * 0 |
Every Sunday at 2 AM | Common slot for weekly maintenance |
30 2 * * * |
Daily at 2:30 AM | Popular for backups — avoids the midnight spike |
0 */6 * * * |
Every 6 hours | Runs at 0:00, 6:00, 12:00, 18:00 |
0 0 1 1,4,7,10 * |
Quarterly (Jan, Apr, Jul, Oct 1st) | Midnight on the first day of each quarter |
0 9 * * 1-5 |
Weekdays at 9 AM | Typical for business-hours notifications |
Schedules cron cannot express directly
Every 30 seconds. Standard cron's minimum granularity is one minute. The usual workaround is two cron entries: one running the command at the top of the minute, and a second that sleeps 30 seconds before running it. For example, in a crontab you would write two lines, both set to * * * * *: the first runs the command directly, and the second runs sleep 30 && /path/to/command. Some platforms like AWS EventBridge support a seconds field natively, making this unnecessary.
Last day of the month. There is no "L" modifier in standard cron. Months have 28, 29, 30, or 31 days, so no single day-of-month value works universally. The common workaround is a daily job that checks whether tomorrow is the 1st: 0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/command. Some extended implementations (Quartz, Spring) support L in the day-of-month field.
Every other Wednesday. Cron has no concept of "every Nth occurrence" of a weekday within a month. You can schedule every Wednesday (0 0 * * 3) and add logic inside the script to check the week number and skip even or odd weeks. Alternatively, use an external scheduler that supports richer recurrence rules.
Why My Cron Didn't Run — Troubleshooting
Cron jobs fail silently more often than they fail loudly. Here are the five most common causes, each of which has tripped up experienced engineers.
Timezone mismatch
The cron daemon runs in the server's system timezone unless configured otherwise. If your server is set to UTC but you wrote the expression thinking in US Eastern time, a job scheduled for 0 9 * * * runs at 9:00 UTC — which is 4:00 or 5:00 AM Eastern depending on daylight saving time. Always check the active timezone with timedatectl or date +%Z on the host. Cloud platforms like GitHub Actions and AWS EventBridge run exclusively in UTC, so convert before writing the expression. For more on timezone conversions, see the Unix Timestamp Converter.
Daylight Saving Time transitions
When clocks spring forward, the skipped hour does not exist. A job scheduled for 2:30 AM in a US timezone will not run on the night DST begins, because 2:30 AM never occurs. Conversely, when clocks fall back, the repeated hour may cause the job to run twice. If your job is time-sensitive, schedule it outside the 1:00 AM–3:00 AM window or pin the server to UTC.
Day-of-week vs. day-of-month (OR logic)
In classic Vixie cron (the implementation on most Linux distributions), when both day-of-month and day-of-week are set to non-wildcard values, the job runs if either condition is true — not both. This is a documented behavior but widely misunderstood. If you write 0 9 15 * 1 intending "the 15th, but only if it's a Monday," the job actually runs every Monday and every 15th. To enforce both conditions, set one field to * and add a conditional check inside the script.
Five-field vs. six-field confusion
Standard cron uses five fields (minute through day-of-week). Some systems — notably Quartz Scheduler, Spring, and AWS EventBridge — prepend a seconds field, making it six fields. If you paste a five-field expression into a six-field system, every field shifts right by one position, producing a wildly different schedule. If you paste a six-field expression into a five-field system, cron rejects it or misparses it. Always verify which format your platform expects.
PATH and environment issues in crontab
Cron does not source your shell profile. The default PATH in a crontab is typically limited to /usr/bin:/bin, so commands that work in your terminal may fail under cron because their binary is not on the path. Fix this by using absolute paths to executables (e.g., /usr/local/bin/node instead of node) or by setting PATH at the top of your crontab file. The same applies to environment variables: if your script depends on DATABASE_URL or similar, export it in the crontab or in a wrapper script. For complex configurations stored in JSON, a JSON formatter helps verify the config file is valid before the job reads it.
Platform-Specific Gotchas
Cron syntax is nearly universal, but the runtime environment varies significantly across platforms. The table below summarizes the key differences you need to know before deploying a schedule.
| Platform | Timezone | Seconds Field | Special Syntax | Gotcha |
|---|---|---|---|---|
| Linux crontab | System TZ | No | Standard 5-field | PATH is not inherited from your shell; set it explicitly |
| GitHub Actions | UTC only | No | Standard 5-field | Uses on.schedule in YAML; shortest practical interval is ~5 min due to queue delays |
| Kubernetes CronJob | UTC default; configurable from v1.27+ via timeZone field |
No | Standard 5-field | Missed schedule policy (concurrencyPolicy, startingDeadlineSeconds) must be set or jobs silently drop |
| AWS EventBridge | UTC | Yes (6-field) | ? required in either day-of-month or day-of-week |
Also supports rate() expressions as an alternative to cron |
| Vercel Cron | UTC | No | Standard 5-field | Hobby plan limited to daily; shorter intervals require Pro plan |
| Cloudflare Workers | UTC | No | Standard 5-field | Configured in wrangler.toml under [triggers]; max 3 triggers per worker on free plan |
When moving a cron expression between platforms, always check three things: whether the platform uses five or six fields, whether it runs in UTC or a configurable timezone, and whether it supports the ? character. Getting any one of these wrong produces a schedule that looks correct but fires at the wrong time.
Reading a Cron Expression Step by Step
To demystify the process, here is how to read an expression from scratch. Take */15 8-17 * * 1-5 and work through it field by field:
- Minute:
*/15— starting at minute 0, every 15 minutes. That means :00, :15, :30, :45. - Hour:
8-17— during hours 8 through 17 (8:00 AM through 5:45 PM, since minute 45 of hour 17 is still matched). - Day of month:
*— every day of the month. - Month:
*— every month. - Day of week:
1-5— Monday through Friday.
Combined: every 15 minutes during business hours on weekdays. This expression produces 40 runs per day (4 per hour across 10 hours) and 200 per work week. Reading each field in isolation, then combining them, is the most reliable way to decode any cron expression. The Cron Explainer tool automates this and shows you the exact next run times.
Writing Better Cron Expressions
Beyond syntax correctness, a few practices reduce the chance of schedule-related incidents:
- Stagger jobs away from :00. Many teams schedule everything at the top of the hour. Shifting to :05 or :15 avoids resource contention and makes logs easier to triage.
- Avoid midnight for non-critical jobs. Midnight is the most congested cron slot on any system. Unless the job genuinely needs to run at day boundary, pick 2:30 AM or 3:15 AM.
- Document the expression inline. Add a comment above the crontab line explaining what it does in plain English. Future you will not remember what
0 */4 1-7 * 1means. - Use UTC for cross-team schedules. If multiple teams or services depend on the same schedule, pin it to UTC and convert locally. This eliminates DST drift and timezone confusion.
- Test before deploying. Paste the expression into an explainer tool and confirm the next 5–10 run times match your expectations. This takes 10 seconds and prevents hours of debugging.
Cron vs. Other Scheduling Approaches
Cron is the right tool for recurring, time-based schedules on a single host or within a platform that accepts cron syntax. It is not the right tool for event-driven workflows (use a message queue), distributed task scheduling across many nodes (use a dedicated scheduler like Celery Beat or Temporal), or sub-second intervals (use a timer in your application code). Understanding where cron fits prevents over-engineering simple schedules and under-engineering complex ones.
For a full set of browser-based utilities, visit the Developer Tools hub.