How-To cron scheduling linux developer

Cron Expressions Explained: How to Schedule Tasks (Complete Guide With Examples)

· 8 min read · Billy C

Cron is the time-based job scheduler built into virtually every Unix-like system. Whether you're rotating logs on a Linux server, triggering a CI pipeline on GitHub Actions, or scheduling a Lambda function on AWS, you'll need to write cron expressions. The syntax is compact and powerful — once you understand the five fields, you can schedule anything.

This guide covers the standard 5-field format, every special character, 15 practical examples you can copy directly, and the differences between cron implementations across platforms. Test your expressions with our Cron Expression Parser to see the next run times instantly.

The 5-Field Cron Format

A standard cron expression has five fields separated by spaces:

┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12 or JAN-DEC) │ │ │ │ ┌───────────── day of week (0-7, 0 and 7 = Sunday, or SUN-SAT) │ │ │ │ │ * * * * *

Each field can contain a single value, a range, a list, or a step value. The combination of all five fields defines when the job runs.

Field Allowed Values Allowed Special Characters
Minute 0-59 * , - /
Hour 0-23 * , - /
Day of Month 1-31 * , - / L W
Month 1-12 or JAN-DEC * , - /
Day of Week 0-7 or SUN-SAT * , - / L #

Special Characters Explained

Character Name Meaning Example
* Wildcard Matches every value in the field * * * * * → every minute
, List Specifies multiple values 0 8,12,18 * * * → 8am, noon, 6pm
- Range Specifies a range of values 0 9-17 * * * → every hour 9am-5pm
/ Step Every nth value */15 * * * * → every 15 minutes
L Last Last day of month or last X-day of month 0 0 L * * → last day of every month
W Weekday Nearest weekday to the given day 0 0 15W * * → nearest weekday to the 15th
# Nth occurrence The nth X-day of the month 0 0 * * 5#3 → third Friday of every month

Note: L, W, and # are not part of the POSIX cron standard. They're available in Quartz Scheduler (Java), Spring, and some cloud platforms, but not in standard Linux crontab. Always check your platform's documentation.

15 Common Cron Expressions (With Plain English)

# Expression Plain English
1 * * * * * Every minute
2 */5 * * * * Every 5 minutes
3 0 * * * * Every hour (at minute 0)
4 0 0 * * * Every day at midnight
5 0 9 * * * Every day at 9:00 AM
6 0 9 * * 1-5 Every weekday at 9:00 AM
7 0 0 * * 0 Every Sunday at midnight
8 0 0 1 * * First day of every month at midnight
9 0 0 1 1 * January 1st at midnight (yearly)
10 30 4 * * * Every day at 4:30 AM
11 0 */2 * * * Every 2 hours
12 0 9,18 * * * Twice daily at 9:00 AM and 6:00 PM
13 */30 9-17 * * 1-5 Every 30 min during business hours (Mon-Fri, 9-5)
14 0 0 1,15 * * 1st and 15th of every month at midnight
15 0 3 * * 6 Every Saturday at 3:00 AM

Not sure if your expression is right? Paste it into our Cron Expression Parser to see the next 10 scheduled run times in a human-readable format.

Cron Across Different Systems

While the core syntax is similar, each platform has its own quirks:

Linux Crontab

The classic cron implementation. Edit your crontab with crontab -e:

# Run backup script every day at 2:30 AM 30 2 * * * /home/user/scripts/backup.sh # Rotate logs every Sunday at midnight 0 0 * * 0 /usr/sbin/logrotate /etc/logrotate.conf # Health check every 5 minutes, log output */5 * * * * curl -s https://myapp.com/health >> /var/log/health.log 2>&1

Linux crontab also supports shorthand macros:

Shorthand Equivalent
@reboot Run once at system startup
@yearly / @annually 0 0 1 1 *
@monthly 0 0 1 * *
@weekly 0 0 * * 0
@daily / @midnight 0 0 * * *
@hourly 0 * * * *

AWS CloudWatch / EventBridge

AWS uses a 6-field format that adds a year field and requires a cron() wrapper. It also uses ? for "no specific value" in the day-of-month or day-of-week field (you can't use * in both):

# AWS EventBridge format: min hour dom month dow year cron(0 9 ? * MON-FRI *) # Every weekday at 9:00 AM UTC cron(0/15 * ? * * *) # Every 15 minutes cron(0 0 1 * ? *) # First of every month at midnight # AWS also supports rate expressions: rate(5 minutes) rate(1 hour) rate(7 days)

GitHub Actions

GitHub Actions uses standard 5-field cron in the schedule trigger. All times are UTC:

on: schedule: - cron: '0 9 * * 1-5' # Weekdays at 9 AM UTC - cron: '0 0 * * 0' # Sundays at midnight UTC

GitHub Actions schedules have a minimum granularity of 5 minutes, and scheduled workflows may be delayed during periods of high load.

Kubernetes CronJob

Kubernetes uses standard 5-field cron syntax in the schedule field:

apiVersion: batch/v1 kind: CronJob metadata: name: db-backup spec: schedule: "30 2 * * *" # Daily at 2:30 AM concurrencyPolicy: Forbid # Don't overlap runs jobTemplate: spec: template: spec: containers: - name: backup image: postgres:16 command: ["pg_dump", "-h", "db", "-U", "app", "mydb"] restartPolicy: OnFailure

Common Pitfalls and How to Avoid Them

1. Timezone Confusion

The single biggest source of cron bugs. Linux crontab runs in the system's local timezone. AWS EventBridge always uses UTC. GitHub Actions always uses UTC. Kubernetes uses the timezone of the kube-controller-manager (usually UTC, but check).

Fix: Always document which timezone your cron expression targets. For a job that should run at 9 AM Eastern (UTC-5), use 0 14 * * * if your system is UTC. Better yet, set your crontab timezone explicitly:

# Linux crontab — set timezone at the top CRON_TZ=America/New_York 0 9 * * * /scripts/morning-report.sh

2. Overlapping Runs

If a job takes 10 minutes to run but is scheduled every 5 minutes, multiple instances will overlap. This can cause data corruption, resource exhaustion, or deadlocks.

Fix: Use a lock file or a concurrency control mechanism:

# Linux — use flock to prevent overlap */5 * * * * /usr/bin/flock -n /tmp/myjob.lock /scripts/long-running-job.sh # Kubernetes — use concurrencyPolicy spec: concurrencyPolicy: Forbid # Skip if previous run still active

3. Day-of-Month AND Day-of-Week

In standard cron, if both day-of-month and day-of-week are specified (not *), the job runs when either condition is true (OR logic), not both. This surprises many people:

# This does NOT mean "run on the 1st if it's a Monday" # It means "run on the 1st of every month AND every Monday" 0 0 1 * 1 # To run only on specific day-of-month AND day-of-week, # add a condition in the script: 0 0 1 * * [ "$(date +\%u)" = "1" ] && /scripts/job.sh

4. Missing Output Capture

Cron jobs run silently by default. If a job fails, you won't know unless you capture the output:

# BAD — silent failures 0 2 * * * /scripts/backup.sh # GOOD — log stdout and stderr 0 2 * * * /scripts/backup.sh >> /var/log/backup.log 2>&1 # GOOD — email output (if mail is configured) MAILTO=admin@example.com 0 2 * * * /scripts/backup.sh

5. Environment Variables

Cron jobs run in a minimal environment — your $PATH, environment variables, and shell configuration from .bashrc or .profile are not loaded. This is why scripts work from the terminal but fail in cron.

# Fix: use absolute paths and set variables in crontab PATH=/usr/local/bin:/usr/bin:/bin NODE_ENV=production 0 9 * * * /usr/local/bin/node /home/user/app/send-report.js

Frequently Asked Questions

What's the smallest interval I can schedule with cron?

Standard cron's smallest unit is one minute (* * * * *). You cannot schedule jobs to run every 30 seconds or every 10 seconds using cron alone. For sub-minute scheduling, you can use workarounds like running a sleep loop inside a cron job, or use a dedicated scheduler like systemd timers (which support second-level precision), or a process manager like PM2 or Supervisor.

How do I list and manage existing cron jobs?

On Linux, use crontab -l to list your cron jobs, crontab -e to edit, and crontab -r to remove all jobs (use with caution). To see cron jobs for other users (as root), use crontab -l -u username. System-wide cron jobs live in /etc/crontab and /etc/cron.d/. Many distributions also have /etc/cron.daily/, /etc/cron.weekly/, and /etc/cron.monthly/ directories where you can drop scripts.

Why does my cron job work manually but fail when scheduled?

Almost always an environment issue. Cron runs with a minimal PATH (usually just /usr/bin:/bin) and doesn't load your shell profile. Solutions: use absolute paths for all commands (e.g., /usr/local/bin/python3 instead of python3), set the PATH variable at the top of your crontab, or source your profile at the start of the script. Also check file permissions — cron runs as your user, but the working directory is usually /, not your home directory.

What's the difference between 5-field and 6-field cron?

Standard POSIX cron uses 5 fields (minute, hour, day-of-month, month, day-of-week). Some systems add a 6th field: Quartz Scheduler (Java) prepends a seconds field, making it 6 or 7 fields. AWS EventBridge appends a year field. Always check which format your platform expects. Our Cron Expression Parser supports the standard 5-field format used by Linux, GitHub Actions, and Kubernetes.

How do DST (Daylight Saving Time) changes affect cron jobs?

When clocks spring forward, a job scheduled during the skipped hour will not run that day. When clocks fall back, a job scheduled during the repeated hour may run twice. UTC-based systems are immune to DST issues, which is why most cloud platforms use UTC. If your crontab uses a local timezone that observes DST, schedule critical jobs outside the 1:00–3:00 AM window, or use UTC and convert manually.