Cron Expressions Explained: A Complete Scheduling Guide
What is Cron?
Cron is one of the most fundamental utilities in the Unix and Linux ecosystem. It is a time-based job scheduler that allows you to automate repetitive tasks by running commands, scripts, or programs at specified intervals. Whether you need to back up a database every night, send a weekly report email, or clean up temporary files every hour, cron is the tool that makes it happen — silently and reliably.
The word "cron" comes from the Greek word chronos, meaning time. The utility was first created by Ken Thompson for Unix Version 7 in the late 1970s. Over the decades, it has evolved through several implementations — the most widely used today being Vixie Cron, written by Paul Vixie in 1987, which became the standard on most Linux distributions.
At the heart of cron is the cron daemon (crond), a background process that starts at system boot and continuously checks a schedule table known as the crontab (cron table). Every minute, the daemon wakes up, reads all crontab entries, compares the current system time against each scheduled expression, and executes any commands whose time has come. This elegant, minute-by-minute polling mechanism has made cron the backbone of automated task management on Unix-like systems for nearly five decades.
Today, the concept of cron has expanded far beyond traditional Unix servers. Cron expressions are used in cloud platforms like AWS, CI/CD pipelines like GitHub Actions, container orchestrators like Kubernetes, and countless application frameworks. Understanding cron syntax is an essential skill for any developer or system administrator working with scheduled tasks.
Tip: Each user on a Unix system can have their own crontab file. System-wide cron jobs are typically stored in /etc/crontab or the /etc/cron.d/ directory, while user-level crontabs are managed with the crontab -e command.
Cron Expression Format
A standard cron expression consists of five fields separated by spaces. Each field represents a unit of time and together they define exactly when a job should run. The general format is:
┌───────────── 분 (minute) : 0–59 │ ┌─────────── 시 (hour) : 0–23 │ │ ┌───────── 일 (day of month) : 1–31 │ │ │ ┌─────── 월 (month) : 1–12 │ │ │ │ ┌───── 요일 (day of week) : 0–6 (0 = Sunday) │ │ │ │ │ * * * * * command_to_execute
Each field can contain a single value, a range, a list of values, or a step value. Let's look at the allowed ranges and descriptions for each field:
| Field | Allowed Values | Description |
|---|---|---|
| Minute | 0–59 | The minute of the hour when the job runs |
| Hour | 0–23 | The hour of the day (24-hour format) |
| Day of Month | 1–31 | The day of the month |
| Month | 1–12 | The month of the year (some systems accept JAN–DEC) |
| Day of Week | 0–6 | Sunday = 0, Monday = 1 ... Saturday = 6 (some systems accept SUN–SAT) |
For example, the expression 30 9 * * * means "at 9:30 AM every day." The first field is 30 (minute), the second is 9 (hour), and the remaining three asterisks mean "every day of the month, every month, every day of the week."
Special Characters
Cron expressions become powerful through a set of special characters that allow you to define complex schedules. Understanding these characters is the key to mastering cron.
Asterisk (*) — Every Value
The asterisk matches all possible valuesfor a given field. It essentially means "don't care" or "every."
# 매시 정각에 실행 (모든 시간, 모든 날) 0 * * * * # 매분 실행 (모든 필드가 와일드카드) * * * * *
Comma (,) — List
The comma lets you specify a list of discrete values in a single field. The job will run at each of the listed values.
# 매일 오전 8시, 정오 12시, 오후 5시에 실행 0 8,12,17 * * * # 1월, 4월, 7월, 10월의 1일에 실행 (분기별) 0 0 1 1,4,7,10 *
Hyphen (-) — Range
The hyphen defines a continuous range of values. All values within the range (inclusive) are matched.
# 평일(월~금)에만 오전 9시에 실행 0 9 * * 1-5 # 매일 근무시간(오전 9시~오후 5시) 동안 매시 정각에 실행 0 9-17 * * *
Slash (/) — Step / Interval
The slash defines a step value. It means "starting from the value before the slash, trigger every N units." When combined with an asterisk, it starts from the minimum value of the field.
# 15분마다 실행 (0, 15, 30, 45분) */15 * * * * # 2시간마다 실행 (0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22시) 0 */2 * * * # 오전 8시부터 3시간 간격으로 실행 (8, 11, 14, 17, 20, 23시) 0 8/3 * * *
Note: You can combine these special characters. For example, 1-30/5in the minute field means "every 5 minutes from minute 1 to 30" — triggering at minutes 1, 6, 11, 16, 21, and 26.
Common Cron Patterns
While cron syntax is flexible enough to express almost any schedule, the vast majority of real-world use cases rely on a handful of common patterns. Here is a reference table of the most frequently used cron expressions:
| Expression | Description |
|---|---|
* * * * * | Every minute |
0 * * * * | Every hour (at minute 0) |
0 0 * * * | Every day at midnight |
0 9 * * 1 | Every Monday at 9:00 AM |
0 0 1 * * | First day of every month at midnight |
0 0 * * 1-5 | Every weekday (Mon–Fri) at midnight |
*/5 * * * * | Every 5 minutes |
0 */6 * * * | Every 6 hours |
0 0 * * 0 | Every Sunday at midnight |
0 0 1 1 * | Once a year on January 1st at midnight |
Many cron implementations also support shorthand aliases for these common patterns: @hourly, @daily, @weekly, @monthly, @yearly, and @reboot (runs once at startup). These aliases make your crontab entries more readable, though not all systems support them.
Cron in Different Systems
While the core five-field cron syntax is universal, different platforms have adopted and extended it in their own ways. Understanding these differences is critical when migrating scheduled tasks between systems.
Linux Crontab
The traditional Linux crontab uses the standard 5-field format. Each user manages their own crontab with the crontab -e command. The system-level crontab at /etc/crontab includes an additional user field specifying which user runs the command.
# 사용자 crontab: 5필드 + 명령어 0 2 * * * /usr/local/bin/backup.sh # 시스템 crontab: 5필드 + 사용자 + 명령어 0 2 * * * root /usr/local/bin/backup.sh
GitHub Actions
GitHub Actions uses the standard 5-field cron syntax within the schedule trigger. An important caveat is that GitHub Actions schedules run in UTC time and may experience delays of up to several minutes during periods of high load. The minimum interval is 5 minutes.
# GitHub Actions 워크플로우에서 cron 스케줄 설정
on:
schedule:
- cron: '0 6 * * 1-5' # 평일 UTC 오전 6시에 실행Kubernetes CronJob
Kubernetes CronJobs use the standard 5-field cron format to schedule containerized tasks. The time zone defaults to the time zone of the kube-controller-manager, though Kubernetes 1.27+ added support for the timeZone field.
# Kubernetes CronJob 매니페스트 예시
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-cleanup
spec:
schedule: "0 3 * * *" # 매일 오전 3시
timeZone: "Asia/Seoul" # 시간대 지정 (K8s 1.27+)
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: busybox
command: ["/bin/sh", "-c", "echo Cleaning up..."]
restartPolicy: OnFailureAWS EventBridge
AWS EventBridge (formerly CloudWatch Events) uses a 6-field cron expression that adds a year field and uses slightly different syntax. It supports the ?character (meaning "no specific value") for the day-of-month or day-of-week fields — one of which must always be ?.
# AWS EventBridge 형식: 분 시 일 월 요일 년 # 매일 오전 10시(UTC)에 실행 cron(0 10 * * ? *) # 평일 오후 6시(UTC)에 실행 cron(0 18 ? * MON-FRI *)
Key difference: AWS EventBridge requires exactly one of the day-of-month or day-of-week fields to be ?. You cannot use * for both simultaneously, which is a common source of confusion when migrating from standard cron. Always double-check the timestamp and time zone to ensure your schedule triggers at the intended time.
Advanced Patterns
Once you are comfortable with the basics, you can combine special characters to create sophisticated schedules. Here are some advanced patterns that address common real-world requirements.
Combining Multiple Values
You can mix commas, ranges, and steps within a single field to create precise schedules:
# 평일 오전 9시, 오후 1시, 오후 6시에 실행 0 9,13,18 * * 1-5 # 매월 1일, 15일 자정에 실행 (반월 스케줄) 0 0 1,15 * * # 10분, 20분, 30분에 실행 (특정 분만) 10,20,30 * * * *
Quarterly Execution
Cron does not have a built-in "quarterly" keyword, but you can achieve it by listing the specific months:
# 매 분기 첫째 날(1/1, 4/1, 7/1, 10/1) 오전 6시에 실행 0 6 1 1,4,7,10 * # 매 분기 마지막 평일에 실행하려면 별도 스크립트로 날짜 검증 필요
Business Hours Only
Restricting jobs to business hours is a common requirement for monitoring, alerting, and reporting tasks:
# 평일 오전 9시~오후 6시 사이 30분마다 실행 */30 9-18 * * 1-5 # 평일 오전 9시~오후 5시 사이 매시 정각에 실행 0 9-17 * * 1-5
Biweekly Execution
Standard cron cannot natively express "every other week." The typical workaround is to use a wrapper script that checks the week number, or to use the day-of-month with a step:
# 격주 실행을 위한 쉘 스크립트 래핑 방식 # crontab에 매주 월요일 실행하도록 설정 0 9 * * 1 /usr/local/bin/biweekly-check.sh # biweekly-check.sh 스크립트 내부 #!/bin/bash WEEK=$(date +%V) if [ $((WEEK % 2)) -eq 0 ]; then # 짝수 주에만 실제 작업 실행 /usr/local/bin/actual-task.sh fi
Cron Debugging Tips
Cron jobs can be notoriously difficult to debug because they run in the background without an interactive terminal. Here are the most common pitfalls and how to avoid them.
Time Zone Issues
One of the most frequent sources of confusion is time zone mismatch. The cron daemon typically runs in the system's local time zone, but cloud services often default to UTC. Always verify which time zone your cron environment uses. You can use a timestamp converter to quickly translate between time zones and confirm your schedule is correct.
# 시스템 시간대 확인 timedatectl # crontab에서 시간대를 명시적으로 설정 (일부 시스템 지원) CRON_TZ=Asia/Seoul 0 9 * * * /usr/local/bin/morning-report.sh
Preventing Overlaps
If a cron job takes longer than its scheduling interval, multiple instances can overlap and compete for resources. Use a lock file mechanism to prevent concurrent execution:
# flock을 사용한 겹침 방지 (권장 방식) */5 * * * * /usr/bin/flock -n /tmp/myjob.lock /usr/local/bin/myjob.sh # 잠금 파일을 직접 관리하는 방식 */5 * * * * /usr/local/bin/run-with-lock.sh
Logging Output
By default, cron sends output (stdout and stderr) via email to the crontab owner. Since most modern systems do not have a local mail agent configured, this output is often silently lost. Always redirect output to a log file:
# stdout과 stderr를 로그 파일에 기록 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # 타임스탬프를 포함한 로깅 0 2 * * * /usr/local/bin/backup.sh 2>&1 | while read line; do echo "$(date): $line"; done >> /var/log/backup.log
Verifying Execution
To confirm that your cron job is actually running, check the system log and verify recent executions:
# cron 실행 로그 확인 (시스템에 따라 경로가 다를 수 있음) grep CRON /var/log/syslog journalctl -u cron --since "1 hour ago" # 현재 사용자의 crontab 목록 확인 crontab -l
Security Best Practices
Cron jobs often run with elevated privileges and handle sensitive operations like database backups, file transfers, and system maintenance. Following security best practices is essential to prevent accidental damage or exploitation.
Principle of Least Privilege
Always run cron jobs with the minimum necessary permissions. Avoid running jobs as root unless absolutely required. Create dedicated service accounts for specific tasks:
# root가 아닌 전용 사용자로 백업 작업 실행 # /etc/crontab 에서 사용자 지정 0 2 * * * backupuser /usr/local/bin/backup.sh # cron.allow / cron.deny로 cron 사용 권한 제어 # /etc/cron.allow — 이 파일에 나열된 사용자만 cron 사용 가능
Output Redirection
Unredirected cron output can fill up mail spools or expose sensitive information. Always redirect output appropriately and ensure log files have proper permissions:
# 출력을 로그 파일로 리다이렉트하고 적절한 권한 설정 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # 로그 파일 권한: 소유자만 읽기/쓰기 # chmod 600 /var/log/backup.log
Environment Variables and PATH
A critical gotcha: cron jobs run with a minimal environment, which is different from your interactive shell. The PATH variable in cron is typically set to /usr/bin:/bin only. This means commands that work fine in your terminal may fail silently in cron. Always use absolute paths for commands and explicitly set environment variables:
# crontab 상단에 PATH 및 환경 변수 설정 PATH=/usr/local/bin:/usr/bin:/bin SHELL=/bin/bash NODE_ENV=production # 절대 경로 사용 (권장) 0 3 * * * /usr/local/bin/node /opt/app/scripts/cleanup.js # 환경 변수를 스크립트 내부에서 소싱하는 방법 0 3 * * * . /home/deploy/.env && /opt/app/scripts/cleanup.sh
Security tip: Never store passwords or API keys directly in crontab entries. Use environment files with restricted permissions (chmod 600) or a secrets manager. If your cron job involves pattern matching on log files or input validation, consider using a regex tester to verify your patterns before deploying them.
Build Cron Expressions with BeautiCode
Writing cron expressions by hand can be error-prone, especially for complex schedules. Misplacing a field or confusing day-of-week numbering can lead to jobs running at the wrong time — or not at all.
BeautiCode's Crontab Generator provides an intuitive visual interface for building and validating cron expressions. Simply select the desired schedule using dropdown menus and toggles, and the tool generates the correct cron syntax instantly. You can also see the next scheduled execution times to verify your expression is correct before deploying it.
Whether you are setting up a simple daily backup, a complex business-hours-only monitoring schedule, or configuring a Kubernetes CronJob, the Crontab Generator eliminates guesswork and helps you get it right the first time. Pair it with the Timestamp Converter to handle time zone conversions, and you have a complete scheduling toolkit at your fingertips.
Frequently Asked Questions
What is the difference between cron and crontab?
Cron is the daemon (background process) that executes scheduled tasks. Crontab (cron table) is the configuration file that defines the schedule and commands. You edit the crontab to tell the cron daemon what to run and when. Think of cron as the engine and crontab as the instruction manual.
Can I run a cron job every 30 seconds?
No. The minimum resolution for cron is one minute. If you need sub-minute scheduling, a common workaround is to schedule the job every minute and add a sleep delay inside the script: * * * * * /path/to/script.sh; sleep 30; /path/to/script.sh. For more precise timing, consider using systemd timers or a dedicated task queue instead of cron.
Why is my cron job not running?
The most common reasons are: (1) PATH issues — cron uses a minimal PATH, so use absolute paths for all commands; (2) Permission errors — ensure the script is executable and the user has permission; (3) Time zone mismatch— verify your system's time zone matches your expectations; (4) Syntax errors — use a tool like the Crontab Generator to validate your expression; (5) Email/output suppression — redirect output to a log file to see error messages.
How do I handle daylight saving time (DST) in cron?
Cron behavior during DST transitions depends on the implementation. In most systems, if the clock springs forward (e.g., 2:00 AM becomes 3:00 AM), jobs scheduled between 2:00 and 2:59 are skipped. When the clock falls back, jobs scheduled during the repeated hour may run twice. To avoid DST issues, schedule critical jobs during hours that are never affected by DST (such as noon or early afternoon), or run them in UTC.
What is the difference between */5 and 0-59/5 in cron?
They are functionally identical. */5is shorthand for "starting from the minimum value, every 5 units," which expands to 0-59/5 for the minute field (triggering at 0, 5, 10, 15, ..., 55). However, 10-30/5 is different — it triggers every 5 minutes but only between minutes 10 and 30 (i.e., 10, 15, 20, 25, 30).
Related Articles
How to Generate Secure Passwords in 2026: A Complete Guide
Learn why strong passwords matter and how to generate secure passwords using entropy, length, and complexity. Includes practical tips and free tools.
2026-03-23 · 8 min readData FormatsJSON vs YAML: When to Use What — A Developer's Guide
Compare JSON and YAML formats with syntax examples, pros and cons, and use case recommendations for APIs, configs, and CI/CD pipelines.
2026-03-23 · 10 min read