If you have ever needed to run a script at 2 AM every Sunday, send a report on the first business day of each month, or purge temp files every fifteen minutes, you have encountered cron. The tiny scheduling daemon that ships with every Unix and Linux system has been quietly running the internet's background tasks since the 1970s — and its five-field expression syntax remains the lingua franca of scheduled automation everywhere from bare-metal servers to Kubernetes, GitHub Actions, and AWS CloudWatch.
The problem? Cron syntax is terse. Writing 15 3 1-7 * 1 from memory and trusting it will fire on the first Monday of the month takes confidence most of us do not have. That is exactly why a cron expression builder exists — a visual tool that lets you pick the schedule you want and translates it into a valid cron string instantly.
In this comprehensive cron expression builder guide, you will learn the five-field syntax inside and out, master every special character, walk through a step-by-step builder workflow using our free cron expression generator, explore cron in CI/CD pipelines, and pick up debugging and timezone tips that will save you hours of frustration.
What Is Cron and Why Does It Matter?
Cron is a time-based job scheduler found in Unix-like operating systems. The name comes from the Greek word chronos (time). A cron job is any command or script that cron runs on a recurring schedule you define with a cron expression.
Cron matters because scheduled automation is everywhere:
- Database backups — nightly dumps to S3 or GCS.
- Log rotation — compress and archive logs before disks fill up.
- Email digests — weekly summary emails to stakeholders.
- Cache invalidation — periodic purges so stale data does not linger.
- Health checks — ping services every minute and alert on failure.
- CI/CD pipelines — nightly builds, scheduled deploys, dependency audits.
- Data pipelines — ETL jobs, report generation, analytics aggregation.
Any time you hear "run this every…" the answer almost always involves a cron expression.
The 5-Field Cron Syntax Explained
A standard cron expression consists of five fields separated by spaces. Each field constrains when the job fires:
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12 or JAN–DEC)
│ │ │ │ ┌───────────── day of week (0–7 or SUN–SAT; 0 and 7 both = Sunday)
│ │ │ │ │
* * * * * The cron daemon evaluates all five fields against the current system time every minute. When every field matches, the associated command runs. Think of it as a logical AND across all five conditions.
Field Reference Table
| Field | Position | Allowed Values | Allowed Special Characters |
|---|---|---|---|
| Minute | 1st | 0–59 | * , - / |
| Hour | 2nd | 0–23 | * , - / |
| Day of Month | 3rd | 1–31 | * , - / |
| Month | 4th | 1–12 or JAN–DEC | * , - / |
| Day of Week | 5th | 0–7 or SUN–SAT | * , - / |
Tip: Some implementations (Quartz, Spring, AWS EventBridge) add a sixth seconds field at the beginning or a year field at the end. Always check your platform's documentation. For the standard Unix crontab, five fields is all you need.
Special Characters in Cron Expressions
Four special characters give cron expressions their flexibility. Mastering them is the key to building any schedule without a cron expression builder — though a builder is still handy for verification.
Asterisk (*) — Any Value
The wildcard matches every possible value for a field. * * * * * means "every minute of every hour of every day of every month on every day of the week" — in other words, every single minute.
Comma (,) — List
Specifies a list of discrete values. 0 8,12,18 * * * fires at 8:00 AM, 12:00 PM, and 6:00 PM every day. You can list as many values as you need, separated by commas with no spaces.
Hyphen (-) — Range
Defines an inclusive range. 0 9-17 * * 1-5 fires at the top of every hour from 9 AM to 5 PM, Monday through Friday. Ranges are cleaner than writing out every value with commas.
Slash (/) — Step
Specifies an interval. */10 * * * * means "every 10 minutes." You can combine a starting value with a step: 5/15 * * * * fires at minutes 5, 20, 35, and 50. The slash is the most powerful character for building recurring schedules.
Non-Standard Characters
| Character | Meaning | Supported By | Example |
|---|---|---|---|
L | Last day of month or last X day of week | Quartz, Spring | 0 0 L * * — last day of month |
W | Nearest weekday to given day | Quartz, Spring | 0 0 15W * * — nearest weekday to 15th |
# | Nth occurrence of a weekday in month | Quartz, Spring | 0 0 * * 5#3 — third Friday |
? | No specific value (either day-of-month or day-of-week) | Quartz, AWS | 0 12 ? * MON — noon every Monday |
Standard Unix crontab does not support L, W, #, or ?. If your target system is a plain Linux crontab, stick to * , - /.
Common Cron Schedule Examples
Here is a reference library of the schedules developers need most often. Bookmark this section or check our cron expression cheat sheet for even more patterns.
Every X Minutes
* * * * * # Every minute
*/5 * * * * # Every 5 minutes
*/10 * * * * # Every 10 minutes
*/15 * * * * # Every 15 minutes
*/30 * * * * # Every 30 minutes Hourly Schedules
0 * * * * # Every hour at minute 0
30 * * * * # Every hour at minute 30
0 */2 * * * # Every 2 hours
0 */6 * * * # Every 6 hours Daily Schedules
0 0 * * * # Midnight every day
0 6 * * * # 6:00 AM every day
30 8 * * * # 8:30 AM every day
0 22 * * * # 10:00 PM every day Weekly Schedules
0 0 * * 0 # Midnight every Sunday
0 9 * * 1 # 9:00 AM every Monday
0 17 * * 5 # 5:00 PM every Friday
0 10 * * 1-5 # 10:00 AM every weekday (Mon–Fri) Monthly Schedules
0 0 1 * * # Midnight on the 1st of every month
0 9 15 * * # 9:00 AM on the 15th of every month
0 0 1 1 * # Midnight on January 1st (yearly)
0 0 1 */3 * # Midnight on the 1st, every 3 months (quarterly) Business-Oriented Schedules
0 9 * * 1-5 # 9 AM weekdays — morning standup trigger
0 0 1,15 * * # Midnight on 1st and 15th — bi-monthly payroll
0 3 * * 0 # 3 AM Sunday — weekly database maintenance
*/5 9-17 * * 1-5 # Every 5 min during business hours, weekdays only Using a Cron Expression Builder: Step-by-Step
While you can certainly hand-write cron expressions, a cron expression builder tool eliminates guesswork and catches mistakes before they reach production. Here is how to use our free online cron generator step by step.
Step 1: Open the Builder
Navigate to the DevToolkit Cron Generator. You will see dropdown menus or input fields for each of the five cron fields: minute, hour, day of month, month, and day of week.
Step 2: Select the Frequency
Start with the broadest frequency that matches your requirement. Do you need something every few minutes, hourly, daily, weekly, or monthly? Many builders offer preset buttons for these common frequencies to get you started quickly.
Step 3: Customize Each Field
Fine-tune the individual fields. For example, if you want a job at 8:30 AM every weekday:
- Minute: Set to
30 - Hour: Set to
8 - Day of Month: Leave as
*(any day) - Month: Leave as
*(every month) - Day of Week: Set to
1-5(Monday through Friday)
The builder instantly shows you the resulting expression: 30 8 * * 1-5.
Step 4: Review the Human-Readable Description
Good cron builders display a plain-English translation of your expression, such as "At 08:30 AM, Monday through Friday." Read this carefully — it is much easier to spot a mistake in English than in cron syntax.
Step 5: Preview the Next Run Times
Most builders show the next 5–10 execution times based on the current date. This is the most reliable verification: if the next run times match your expectations, the expression is correct. Pay attention to timezone settings — more on that below.
Step 6: Copy and Deploy
Copy the generated expression and paste it into your crontab, CI/CD config, or cloud scheduler. Always test in a staging environment before deploying to production.
Managing Crontab on Linux and macOS
Once you have built your cron expression, you need to install it in the system's cron scheduler. The crontab command is the standard interface.
Essential Crontab Commands
# View your current crontab
crontab -l
# Edit your crontab (opens in $EDITOR)
crontab -e
# Remove your entire crontab (use with extreme caution!)
crontab -r
# Edit another user's crontab (requires root)
sudo crontab -u username -e Crontab File Format
Each line in a crontab file follows this structure:
# Environment variables (optional)
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
[email protected]
# Schedule entries
# min hour dom month dow command
0 3 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
*/5 * * * * /usr/bin/curl -s https://example.com/health > /dev/null
0 9 * * 1 /home/user/scripts/weekly-report.py Pro tip: Always redirect output with >> logfile 2>&1 so you have a trail to debug when things go wrong. Without redirection, cron emails the output to the user's local mailbox — which most people never check.
System-Wide Cron Directories
In addition to per-user crontabs, Linux distributions provide system-wide cron directories:
/etc/cron.d/— drop-in cron files (include a username field after the schedule)./etc/cron.hourly/— scripts here run once per hour./etc/cron.daily/— scripts here run once per day./etc/cron.weekly/— scripts here run once per week./etc/cron.monthly/— scripts here run once per month.
To use these directories, simply place an executable script inside them. No cron expression needed — the scheduling is handled by the directory name.
Cron Expressions in CI/CD Pipelines
Modern DevOps heavily leverages cron expressions outside of traditional crontab. Here is how cron syntax appears in popular CI/CD platforms.
GitHub Actions
GitHub Actions uses the schedule trigger with standard 5-field cron expressions. All times are in UTC.
name: Nightly Build
on:
schedule:
- cron: '0 3 * * *' # 3:00 AM UTC every day
- cron: '0 12 * * 1' # Noon UTC every Monday
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build && npm test Important: GitHub Actions scheduled workflows can be delayed during periods of high load. Do not rely on them for time-critical operations. Also note that scheduled workflows only run on the default branch.
GitLab CI/CD
GitLab supports pipeline schedules configured through the UI or API. The cron syntax is identical to standard five-field expressions:
# In GitLab UI: Pipeline Schedules → New Schedule
# Cron: 0 2 * * *
# Timezone: America/New_York
# Target branch: main Jenkins
Jenkins uses a cron-like syntax in the triggers block. It adds a special H symbol that distributes load by hashing the job name:
pipeline {
triggers {
cron('H 3 * * *') // Sometime during the 3 AM hour
{
stages {
stage('Build') {
steps {
sh 'make build'
{
{
{
{ AWS CloudWatch Events / EventBridge
AWS uses a six-field cron format that adds a year field and uses ? for either day-of-month or day-of-week:
# AWS cron format: minute hour day-of-month month day-of-week year
cron(0 3 * * ? *) # 3 AM UTC every day
cron(0 12 ? * MON-FRI *) # Noon UTC weekdays
cron(0 0 1 * ? *) # Midnight on the 1st of every month
Notice the ? character — AWS requires it in either the day-of-month or day-of-week field. Use our cron expression builder to generate the correct format for your target platform.
Kubernetes CronJobs
Kubernetes CronJobs use standard five-field cron syntax in YAML manifests:
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
spec:
schedule: "0 2 * * *" # 2 AM daily
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
command: ["./run-backup.sh"]
restartPolicy: OnFailure Debugging Cron Jobs: Why Your Job Did Not Run
Cron jobs fail silently more often than any other part of a system. Here is a systematic checklist for debugging.
1. Check the Expression
Paste your cron expression into a cron expression builder and verify the next run times. A surprisingly common mistake is swapping the day-of-month and month fields, or using 24-hour time incorrectly (there is no hour 24 — midnight is 0).
2. Inspect the Cron Log
# On most Linux systems
grep CRON /var/log/syslog
# On CentOS/RHEL
grep CRON /var/log/cron
# On macOS
log show --predicate 'process == "cron"' --last 1h The cron log tells you whether cron attempted to execute your command. If you see no entry at the expected time, the expression itself is wrong or the cron daemon is not running.
3. Verify the Environment
Cron runs commands in a minimal environment. Your shell profile (~/.bashrc, ~/.zshrc) is not loaded. Common issues include:
- Missing PATH: Commands like
node,python3, ordockermay not be found. Use full paths like/usr/local/bin/node. - Missing environment variables: API keys, database URLs, and other secrets are not available. Source an env file at the start of your script or set variables in the crontab with
ENV_VAR=valuelines. - Wrong working directory: Cron starts in the user's home directory. Use
cd /path/to/project &&before your command, or use absolute paths throughout your script.
4. Check Permissions
Ensure the script is executable (chmod +x script.sh) and that the cron user has permission to access all files and directories the script touches. Also verify the user is not in /etc/cron.deny.
5. Capture Output
Always redirect both stdout and stderr to a log file:
0 3 * * * /home/user/backup.sh >> /home/user/logs/backup.log 2>&1 Without this redirection, errors vanish into the void (or an unread local mailbox). This is the single most important debugging practice for cron jobs.
6. Test Manually First
Before scheduling, run the exact command that cron will execute in a clean shell. Simulate cron's minimal environment:
env -i HOME=$HOME SHELL=/bin/bash /bin/bash -c '/home/user/scripts/backup.sh' If the command fails here, it will fail in cron too.
Timezone Considerations for Cron
Timezone handling is one of the most treacherous aspects of cron scheduling. Get it wrong and your 3 AM backup runs at 3 PM — or not at all when daylight saving time shifts.
System Crontab
Traditional crontab uses the system timezone (whatever timedatectl or /etc/timezone reports). If your server is set to UTC but you want jobs to run at 9 AM Eastern, you must manually convert: 9 AM ET is 14:00 UTC (or 13:00 during EDT). This is error-prone.
Per-Job Timezone (Modern Systems)
Some modern cron implementations (like systemd timers, Kubernetes CronJobs v1.25+, and cloud schedulers) let you set a timezone per job:
# Kubernetes CronJob with timezone (v1.25+)
spec:
schedule: "0 9 * * 1-5"
timeZone: "America/New_York" # GitHub Actions — always UTC, no timezone override
# Convert manually or use a step to check the time
on:
schedule:
- cron: '0 14 * * 1-5' # 9 AM ET in winter (UTC-5) Daylight Saving Time Pitfalls
When clocks spring forward, a 2:30 AM job may not fire because 2:30 AM does not exist that night. When clocks fall back, a 1:30 AM job may fire twice. Best practices:
- Schedule critical jobs in UTC to avoid DST surprises entirely.
- Avoid scheduling jobs during the 1:00–3:00 AM window in timezones that observe DST.
- Use idempotent scripts so running twice causes no harm.
- If you must use local time, use a scheduler that supports timezone-aware scheduling (systemd timers, Kubernetes with
timeZone).
Advanced Cron Patterns and Techniques
Combining Ranges, Lists, and Steps
You can mix special characters within a single field for powerful combinations:
# Every 15 minutes during business hours on weekdays
*/15 9-17 * * 1-5
# At minute 0 and 30, hours 8–12 and 14–18
0,30 8-12,14-18 * * *
# Every 2 hours starting at 1 AM
0 1/2 * * * # Fires at 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 Running on Specific Months
# Quarterly — 1st day of Jan, Apr, Jul, Oct at midnight
0 0 1 1,4,7,10 *
# Every weekday in December (holiday prep)
0 9 * 12 1-5 Locking to Prevent Overlap
If a cron job takes longer than its interval, you can end up with overlapping instances that corrupt data or exhaust resources. Use flock to prevent this:
# Only one instance runs at a time
*/5 * * * * /usr/bin/flock -n /tmp/myjob.lock /home/user/scripts/myjob.sh
The -n flag tells flock to exit immediately if the lock is already held, so the second instance simply skips rather than queuing up.
Cron Alternatives Worth Knowing
- systemd timers: More flexible than cron on modern Linux. Support monotonic (boot-relative) timers, randomized delays, and better logging via
journalctl. - at: For one-time scheduled jobs.
at now + 2 hoursruns a command once, two hours from now. - Celery Beat: Python-based periodic task scheduler, ideal for Django and Flask applications.
- node-cron: NPM package for running cron-scheduled functions inside Node.js processes.
Best Practices for Production Cron Jobs
After years of collective experience, these practices separate reliable cron setups from fragile ones:
- Always use a cron expression builder to verify. Even experienced sysadmins double-check with a tool. Our cron generator shows next run times so you can confirm before deploying.
- Log everything. Redirect stdout and stderr to timestamped log files. Rotate logs with
logrotate. - Use absolute paths. Never rely on
PATHor relative paths in cron commands. - Make scripts idempotent. If a script runs twice by accident, the result should be the same as running once.
- Set up alerting. Use
MAILTOin crontab, or have your script send a Slack/Discord/PagerDuty alert on failure. - Use lock files. Prevent overlapping executions with
flockor a PID file. - Document each job. Add a comment above every crontab entry explaining what it does and who owns it.
- Version-control your crontab. Export it periodically with
crontab -l > crontab.bakand commit it to your repo. - Prefer UTC for servers. This eliminates DST headaches and makes cross-timezone coordination simpler.
- Test in staging first. Never deploy a new cron job directly to production. Test with a short interval (e.g., every minute), verify it works, then set the real schedule.
Frequently Asked Questions
What is a cron expression?
A cron expression is a string of five fields (minute, hour, day of month, month, day of week) that defines a recurring schedule for automated tasks. Each field specifies when a job should run, and the job fires when all fields match the current time.
How do I create a cron expression without memorizing the syntax?
Use a cron expression builder tool. You select your desired schedule from dropdown menus or input fields, and the tool generates the correct cron string. It also shows you the next execution times so you can verify the schedule is right.
What does */5 * * * * mean?
It means "every 5 minutes." The */5 in the minute field tells cron to fire at minutes 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, and 55 of every hour, every day.
Why did my cron job not run?
The most common reasons are: incorrect cron expression syntax, missing environment variables or PATH entries (cron uses a minimal environment), insufficient file permissions, the cron daemon not running, or the script failing silently. Check the cron log (/var/log/syslog on Ubuntu) and ensure your script outputs errors to a log file.
What timezone does cron use?
By default, cron uses the system timezone configured on the server. GitHub Actions always uses UTC. Cloud platforms like AWS and GCP let you specify a timezone. Best practice for production is to run servers in UTC and convert times manually, or use a scheduler that supports explicit timezone configuration.
Can I run a cron job every 30 seconds?
Standard cron's smallest granularity is one minute. To run something every 30 seconds, you can create two cron entries — one that runs at the start of each minute and one delayed by 30 seconds using sleep: * * * * * /path/to/script.sh and * * * * * sleep 30 && /path/to/script.sh. For sub-minute scheduling, consider using systemd timers or a dedicated task queue instead.
What is the difference between cron and crontab?
Cron is the background daemon (service) that reads schedule files and runs jobs at the specified times. Crontab (cron table) is the file that contains the schedule entries, and also the command (crontab -e) used to edit that file. You write cron expressions in your crontab, and the cron daemon executes them.
How do I schedule a cron job in GitHub Actions?
Add a schedule trigger to your workflow YAML file with a cron key. For example, on: schedule: - cron: '0 3 * * *' runs the workflow at 3 AM UTC daily. Remember that GitHub Actions only supports UTC and may delay scheduled runs during high-demand periods.
Conclusion
Cron expressions are deceptively simple — five fields, four special characters, infinite scheduling possibilities. Whether you are setting up a midnight database backup, a weekday-only health check, or a quarterly reporting job, the syntax remains the same. The trick is getting it right the first time.
A cron expression builder takes the guesswork out of the equation. Instead of staring at 0 */4 1-15 * 1-5 and wondering if it does what you think, you get instant visual feedback, plain-English translations, and previews of upcoming execution times. Use our free cron expression generator to build, validate, and copy your next cron schedule in seconds.
For a quick-reference card you can keep open in another tab, check out our cron expression cheat sheet with dozens of copy-paste-ready patterns.