Cron Expression Generator — Build Schedules in Seconds
Contents
- What Is a Cron Expression?
- The Five Fields Explained
- 20 Common Cron Patterns You'll Actually Use
- Special Characters and What They Do
- Cron Across Platforms: crontab vs GitHub Actions vs Kubernetes vs systemd
- Common Mistakes That Break Cron Jobs
- Debugging Cron Jobs
- Why Use a Visual Cron Generator?
- FAQ
Cron expressions are one of those things that every developer needs eventually and nobody remembers the syntax for. The five-field format is deceptively simple — minute, hour, day, month, weekday — but the moment you need "every 15 minutes on weekdays" or "the first Monday of each month at 9 AM," you're searching StackOverflow.
This guide covers cron syntax from the ground up, gives you 20 ready-to-use patterns for real scheduling needs, and explains the differences between crontab, GitHub Actions, Kubernetes CronJobs, and systemd timers. At the end, there's a free visual generator that builds the expression for you.
What Is a Cron Expression?
A cron expression is a compact string that defines when a task should run. The name comes from the Greek word chronos (time), and the cron daemon has been a core Unix utility since the 1970s. Despite being over 50 years old, cron expressions remain the standard scheduling format across modern infrastructure — from simple server scripts to Kubernetes cluster orchestration.
A standard cron expression has five fields separated by spaces:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *
Each field can be a specific value, a range, a list, a step, or a wildcard. Combined, they describe virtually any recurring schedule you'd need in production.
The Five Fields Explained
| Field | Allowed Values | Description |
|---|---|---|
| Minute | 0–59 | Which minute of the hour the job fires |
| Hour | 0–23 | 24-hour format. 0 = midnight, 13 = 1 PM |
| Day of Month | 1–31 | Calendar day. Not all months have 31 days — cron silently skips invalid dates |
| Month | 1–12 or JAN–DEC | Which month(s). Names are case-insensitive in most implementations |
| Day of Week | 0–7 or SUN–SAT | 0 and 7 both represent Sunday. Names work in crontab but not everywhere |
*), cron runs the job when either condition is met, not both. This catches many people off guard. 0 9 15 * 1 runs at 9 AM on the 15th of every month and every Monday — not just Mondays that fall on the 15th.
20 Common Cron Patterns You'll Actually Use
| Expression | Schedule |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/15 * * * * | Every 15 minutes |
0 * * * * | Every hour (at :00) |
30 * * * * | Every hour at :30 |
0 */2 * * * | Every 2 hours |
0 */6 * * * | Every 6 hours |
0 0 * * * | Daily at midnight |
0 9 * * * | Daily at 9:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 0 * * 0 | Weekly (Sunday midnight) |
0 9 * * 1 | Every Monday at 9:00 AM |
0 0 1 * * | First day of every month at midnight |
0 0 1 1 * | January 1st at midnight (yearly) |
0 0 1,15 * * | 1st and 15th of every month |
0 8-17 * * 1-5 | Every hour during business hours (Mon–Fri 8 AM–5 PM) |
*/10 * * * 1-5 | Every 10 minutes on weekdays |
0 0 * * 6,0 | Weekends at midnight |
0 6,12,18 * * * | Three times daily (6 AM, noon, 6 PM) |
0 0 L * * | Last day of every month (not standard — supported by Quartz, Spring) |
Bookmark this table. These 20 patterns cover the vast majority of real-world scheduling needs.
Special Characters and What They Do
Asterisk (*)
Matches every possible value in a field. * * * * * means every minute of every hour of every day. Think of it as "any."
Comma (,)
Specifies a list of values. 0 9,12,17 * * * runs at 9 AM, noon, and 5 PM. You can list as many values as needed.
Hyphen (-)
Defines a range. 0 9-17 * * * runs every hour from 9 AM through 5 PM inclusive. Ranges work in any field.
Slash (/)
Defines a step interval. */10 * * * * means every 10th minute (0, 10, 20, 30, 40, 50). You can combine slashes with ranges: 10-50/10 * * * * fires at minutes 10, 20, 30, 40, and 50.
Combining characters
Characters can be combined within a single field. 0 9-17/2 * * 1-5 means every 2 hours from 9 AM to 5 PM on weekdays — that's 9, 11, 13, 15, 17. This kind of compound expression is exactly where a visual generator saves you from trial and error.
Cron Across Platforms
The five-field cron expression is universal, but every platform wraps it differently. Here's how the same schedule — "every Monday at 9 AM" — looks across four major platforms.
crontab (Linux/macOS)
The original. Edit with crontab -e and add one line per job:
0 9 * * 1 /usr/bin/python3 /opt/scripts/weekly_report.py >> /var/log/report.log 2>&1
Always redirect output and capture stderr. Cron jobs run with a minimal environment — no PATH, no shell aliases. Use absolute paths for everything.
GitHub Actions
GitHub Actions uses the schedule trigger with standard cron syntax:
on:
schedule:
- cron: '0 9 * * 1'
jobs:
weekly-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python scripts/report.py
Two important caveats: schedules run in UTC only, and GitHub may delay execution by up to 15 minutes during high-load periods. Don't rely on GitHub Actions cron for time-critical tasks.
Kubernetes CronJob
Kubernetes wraps cron expressions in a CronJob resource:
apiVersion: batch/v1
kind: CronJob
metadata:
name: weekly-report
spec:
schedule: "0 9 * * 1"
jobTemplate:
spec:
template:
spec:
containers:
- name: report
image: python:3.12
command: ["python", "/app/report.py"]
restartPolicy: OnFailure
Kubernetes CronJobs use the cluster's timezone by default. Set spec.timeZone (available since Kubernetes 1.27) if you need a specific timezone.
systemd timers
Systemd uses a different syntax entirely — calendar event expressions instead of five-field cron:
# /etc/systemd/system/weekly-report.timer
[Unit]
Description=Weekly report timer
[Timer]
OnCalendar=Mon *-*-* 09:00:00
Persistent=true
[Install]
WantedBy=timers.target
The Persistent=true directive is important — it ensures the job runs after a missed schedule (e.g., if the server was down on Monday). Standard crontab doesn't do this without a tool like anacron.
Common Mistakes That Break Cron Jobs
1. Forgetting about timezones
Crontab uses the system timezone. GitHub Actions uses UTC. Kubernetes uses the cluster timezone (usually UTC). If your "9 AM" job runs at 2 AM, you have a timezone mismatch. Always know what timezone your cron daemon uses and convert accordingly.
2. Missing PATH
Cron jobs run with a minimal environment. The PATH variable typically includes only /usr/bin:/bin. If your script calls python3, node, or npm, use the full path (/usr/bin/python3) or set PATH explicitly at the top of your crontab.
3. Overlapping executions
If your cron job takes 7 minutes to run but is scheduled every 5 minutes, you'll get overlapping executions that compete for resources. Use a lock file (flock) or check for a running instance before starting:
*/5 * * * * /usr/bin/flock -n /tmp/myjob.lock /opt/scripts/myjob.sh
4. Swallowing errors
Cron sends output to the system's mail by default, which most people never check. Always redirect output to a log file and capture stderr:
0 * * * * /opt/scripts/job.sh >> /var/log/job.log 2>&1
5. Using day-of-month AND day-of-week
As mentioned above, specifying both creates an OR condition, not AND. If you want "the first Monday of the month," you can't express that in standard cron. You need wrapper logic in your script to check the date.
6. Editing the wrong crontab
System-wide cron jobs go in /etc/crontab or /etc/cron.d/. User cron jobs go in user crontabs via crontab -e. These have different formats — the system crontab has a username field between the schedule and the command. Mixing them up causes silent failures.
Debugging Cron Jobs
When a cron job doesn't fire, work through this checklist:
- Verify the expression. Paste it into a visual cron generator to confirm it means what you think. Off-by-one errors in the hour or day field are extremely common.
- Check the cron daemon. Run
systemctl status cron(orcrondon RHEL/CentOS). If the daemon isn't running, nothing fires. - Check the logs. Look in
/var/log/syslogor/var/log/cronfor entries showing when your job was triggered. If there's no log entry, the expression is wrong or the crontab wasn't loaded. - Test the command manually. Run the exact command from the crontab entry in a terminal with a minimal environment. If it fails interactively, it'll fail in cron too.
- Check permissions. The script must be executable (
chmod +x), and the cron user must have access to all files the script reads or writes. - Check disk space. A full disk prevents log files from being written, which can cause jobs to fail silently.
* * * * * (every minute) so you get fast feedback. Just remember to change it back.
Why Use a Visual Cron Generator?
Cron syntax is compact but opaque. A visual generator solves three problems at once:
- Instant validation. You see the next 5 execution times immediately. No guessing whether
0 */4 * * *runs at midnight or skips it. - Multi-format export. You build one schedule and copy it as crontab syntax, GitHub Actions YAML, Kubernetes CronJob spec, or systemd calendar expression. No manual translation between formats.
- Clickable examples. Start from a common pattern like "weekdays at 9 AM" and modify from there, instead of writing from scratch.
The Cron Expression Generator on helloandy.net does all three — free, no login, with 20 preset patterns and a visual timeline bar showing when your job fires across a 24-hour period.
Try the Cron Expression Generator — Free
Build cron schedules visually. 20 clickable presets, multi-format export (crontab, GitHub Actions, Kubernetes, systemd), and a 24-hour timeline showing exactly when your job fires.
Try it free →Frequently Asked Questions
What is a cron expression?
A cron expression is a string of five fields that defines a recurring schedule: minute, hour, day of month, month, and day of week. It's used by Unix crontab, GitHub Actions, Kubernetes CronJobs, and many other scheduling systems. The format has been a standard since the 1970s and remains the dominant way to define schedules in software infrastructure.
How do I run a cron job every 5 minutes?
Use the expression */5 * * * *. The */5 in the minute field means "every 5th minute" — firing at 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, and 55 minutes past each hour. The remaining asterisks mean every hour, every day, every month, every weekday.
What's the difference between crontab and systemd timers?
Crontab uses the traditional five-field syntax and runs via the cron daemon. Systemd timers use calendar event expressions (like OnCalendar=*-*-* 09:00:00) and integrate with systemd's service management. Systemd timers offer logging, dependency management, persistent scheduling (catches up on missed runs), and randomized delays. Both achieve the same goal but systemd timers are more powerful on modern Linux systems.
Can I use a cron expression in GitHub Actions?
Yes. Use the schedule trigger in your workflow YAML file: schedule: - cron: '0 9 * * 1'. Important notes: GitHub Actions cron schedules always run in UTC, may be delayed up to 15 minutes during high-load periods, and only run on the default branch.
How do I express "first Monday of the month" in cron?
You can't do it with a standard cron expression alone. The standard format doesn't support "nth weekday of month" logic. You need to schedule the job every Monday (0 9 * * 1) and add a date check in your script: [ $(date +%d) -le 7 ] || exit 0. Some extended cron implementations (like Quartz) support additional syntax for this.
Related reading: Free API Tester · Free AI Regex Generator · Free AI Coding Assistant
— Andy