Skip to content
블로그로 돌아가기
튜토리얼

Crontab 치트시트: 50+ Cron 표현식과 현대 스케줄러 가이드 | Go Tools

온라인 crontab 치트시트: 50+ cron 표현식, 다섯 필드 문법, 일/요일 OR 함정, 타임존 디버깅, Kubernetes/GitHub/AWS 비교까지 한 페이지에 모두.

13 분 소요

Crontab 치트시트: 50+ Cron 표현식, 문법, 현대 스케줄러 가이드

Cron 표현식은 다섯 필드(분, 시, 일, 월, 요일) 뒤에 명령이 붙는 형태입니다. 이 문법은 1979년부터 Unix 스케줄링을 맡아 왔고, 지금은 Kubernetes CronJob, GitHub Actions, AWS EventBridge, Vercel cron 트리거에서도 그대로 동작합니다. 한 번 익히면 어디서든 스케줄을 짤 수 있습니다.

이 페이지는 당장 표현식이 필요한 개발자를 위한 자료입니다. Linux 작업을 짜는 중이거나, Kubernetes CronJob을 작성하거나, GitHub Actions 트리거를 설정하거나, 5분 주기 작업이 왜 한 시간에 한 번만 실행되는지 들여다보고 있을 수 있습니다. 복사해서 붙여 넣을 표현식이 필요하면 빠른 참조 표로 스크롤하고, 필드 규칙은 문법 해부 섹션을 보고, 표현식을 실시간으로 검증하고 싶다면 Crontab 생성기 — 브라우저에서 동작하는 프라이버시 우선 crontab guru 대체재 — 를 여세요.

Cron 표현식 빠른 참조 표

실제 스케줄링 수요의 약 90%를 덮는 30개 표현식입니다. 모두 유효한 POSIX 다섯 필드 cron이므로 crontab -e, Kubernetes schedule:, 또는 GitHub Actions cron: 어디에나 붙여 넣을 수 있습니다.

일정Cron 표현식평이한 설명
매 분* * * * *매 분, 하루 종일, 매일
5분마다*/5 * * * *분 0, 5, 10, …, 55
15분마다*/15 * * * *분 0, 15, 30, 45
30분마다*/30 * * * *분 0과 30
매시0 * * * *매시 정각
2시간마다0 */2 * * *시 0, 2, 4, …, 22
6시간마다0 */6 * * *시 0, 6, 12, 18
하루 두 번 (오전 9시 + 오후 9시)0 9,21 * * *9시와 21시의 0분
평일 오전 9시0 9 * * 1-5월-금 09:00
주말 오전 9시0 9 * * 0,6토·일 09:00
매일 자정0 0 * * *매일 00:00
매일 오전 2:3030 2 * * *트래픽이 적은 배치 시간대
매주 월요일 오전 9시0 9 * * 1월요일 09:00
매주 금요일 오후 5시0 17 * * 5금요일 17:00
매주 일요일 자정0 0 * * 0@weekly와 동일
매월 1일 자정0 0 1 * *1일 00:00 — @monthly와 동일
매월 15일 정오0 12 15 * *월중 급여 처리 시간대
말일 체크 (래퍼)0 0 28-31 * * + 스크립트날짜 체크가 필요
분기 단위 (1/4/7/10월 1일)0 0 1 JAN,APR,JUL,OCT *각 분기 첫날
연 1회 (1월 1일)0 0 1 1 * 또는 @yearly새해 자정
평일 9-5시 5분마다*/5 9-17 * * 1-5업무 시간 폴링
주말 30분마다*/30 * * * 0,6토·일 모니터링
시간당 두 번, 15분과 45분15,45 * * * *:00 정각 몰림 회피
첫째 주 월요일 (래퍼)0 9 1-7 * 1 + AND 체크래퍼 필요 (아래 참조)
매크로@hourly @daily @weekly @monthly @yearly비표준이지만 폭넓게 지원
부팅 시에만@reboot비표준, vixie cron 전용

이 중 아무거나 Crontab 생성기에 붙여 넣으면 다음 다섯 번 실행 시각이 미리 표시됩니다. 배포 전에 가장 빨리 끝낼 수 있는 점검입니다.

Cron 문법 해부, 다섯 필드

Cron 표현식은 공백으로 구분된 다섯 필드와 명령으로 이뤄지며, 각 필드가 스케줄의 한 자리를 차지합니다. 이 가이드에 등장하는 모든 스케줄러에서 cron 표현식 문법의 핵심은 같습니다.

┌──────────── minute       (0 - 59)
│ ┌────────── hour         (0 - 23)
│ │ ┌──────── day-of-month (1 - 31)
│ │ │ ┌────── month        (1 - 12 or JAN-DEC)
│ │ │ │ ┌──── day-of-week  (0 - 6 or SUN-SAT; 0 and 7 both mean Sunday)
│ │ │ │ │
* * * * *  command-to-run

기억법은 “My Hat Doesn’t Match Wendy’s”입니다(Minute, Hour, Day-of-month, Month, Weekday). 왼쪽에서 오른쪽으로, 작은 단위에서 큰 단위로 갑니다.

필드별 허용 값

필드범위별칭비고
0-59없음0은 “정각”
0-23없음24시간제; 0은 자정, 12는 정오
1-31없음해당 월에 없는 날짜는 조용히 실행되지 않음(2월 31일)
1-12JAN, FEB, MAR, …, DEC대소문자 구분 없음
요일0-7SUN, MON, TUE, …, SAT07 모두 일요일

연산자 상세

다섯 개의 연산자가 표준 cron 표현식 전체를 덮습니다:

연산자의미예시전개 결과
*모든 값* * * * *매 분
,목록0 9,12,17 * * *09:00, 12:00, 17:00
-닫힌 범위0 9-17 * * *09:00부터 17:00까지 매시
/스텝*/15 * * * *분 0, 15, 30, 45
혼합결합0 9-12,14-17 * * *오전 + 오후, 점심 시간 제외

스텝 연산자는 따로 짚어 둬야 합니다. */N은 해당 필드의 최솟값을 기준으로 고정되고, 현재 시각을 기준으로 잡지 않습니다. */15는 “매시 0, 15, 30, 45분”이라는 뜻이지 “지금부터 15분마다”가 아닙니다. 12:03에 저장하면 다음 실행은 12:15입니다. 와일드카드가 아닌 기준값과 함께 쓰는 5/15는 “5에서 시작해 그 뒤로 15마다”로 읽히며 분 5, 20, 35, 50을 의미합니다.

이름으로 쓴 월과 요일

월과 요일은 이름으로 쓸 수 있으며 대소문자를 가리지 않습니다:

0 0 1 JAN,APR,JUL,OCT *    # 각 분기 첫날
0 9 * * MON-FRI            # 평일 오전 9시
0 17 * * FRI               # 금요일 오후 5시

이름 형식은 코드 리뷰에서 더 읽기 쉽고, 숫자 형식은 이식성이 조금 더 좋습니다. 프로젝트별로 한 가지 스타일을 골라 쓰세요.

비표준 매크로: @reboot, @daily와 그 친구들

대부분의 cron 구현은 여섯 개의 단축 매크로를 받아들입니다:

매크로전개 결과의미
@yearly / @annually0 0 1 1 *1년에 한 번, 1월 1일 자정
@monthly0 0 1 * *매월 1일 자정
@weekly0 0 * * 0매주 일요일 자정
@daily / @midnight0 0 * * *매일 자정
@hourly0 * * * *매시 정각
@reboot(특수)cron 데몬이 시작될 때 한 번

이 매크로들은 비표준입니다. vixie cron과 cronie는 지원하지만 Kubernetes CronJob, GitHub Actions, AWS EventBridge는 지원하지 않습니다. 이식성을 챙기려면 다섯 필드 형식으로 쓰세요. @reboot은 cron 데몬이 init 프로세스가 아닐 수 있는 컨테이너 환경에서는 거의 작동하지 않습니다.

50+ 복사·붙여넣기용 Cron 표현식 (용도별 분류)

빠른 참조 표가 일반적인 경우를 덮었다면, 이 섹션은 여섯 갈래로 나눠 더 촘촘한 cron 작업 예시를 다룹니다.

N분마다

* * * * *          # 매 분
*/2 * * * *        # 2분마다
*/5 * * * *        # 5분마다 — "cron expression every 5 minutes" 사례
*/10 * * * *       # 10분마다
*/15 * * * *       # 15분마다
*/30 * * * *       # 30분마다
0,30 * * * *       # 명시적으로 0분과 30분 (*/30과 동일)
*/45 * * * *       # 주의: 0분과 45분에만 실행된 뒤 다음 시간에서 반복

*/45은 흔한 함정입니다. 분은 0-59이므로 0과 45에 실행된 다음 시간이 바뀌면서 다시 시작합니다. 진짜 45분 주기를 원한다면 외부 워커가 필요합니다.

시간 단위 변형

0 * * * *          # 매시 :00
30 * * * *         # 매시 :30
0 */2 * * *        # 2시간마다, 짝수 시각
0 */6 * * *        # 6시간마다
0 */12 * * *       # 하루 두 번, 00:00과 12:00
15 */2 * * *       # 2시간마다, 15분 오프셋 (:00 정각 몰림 회피)

매일 특정 시각

0 0 * * *          # 자정 (= @daily / @midnight)
30 2 * * *         # 02:30 — 트래픽이 적은 배치 시간대
0 9 * * *          # 09:00
45 23 * * *        # 23:45 — 하루 마감 집계
0 9,12,17 * * *    # 하루 세 번
0 9-17 * * *       # 09:00부터 17:00까지 매시

주간 스케줄

0 9 * * 1-5        # 평일 오전 9시
0 9 * * 0,6        # 주말 오전 9시
0 18 * * 5         # 금요일 오후 6시
0 0 * * 0          # 일요일 자정 (= @weekly)
0 9 * * MON,WED,FRI # 월/수/금 오전 9시
*/30 9-17 * * 1-5  # 평일 업무 시간 30분마다

월간·분기 단위

0 0 1 * *          # 매월 1일 자정 (= @monthly)
0 0 15 * *         # 15일 — 급여 처리 시간대
0 0 1,15 * *       # 1일과 15일 — 반월(半月) 주기
0 0 1 */3 *        # 분기 단위: 1월, 4월, 7월, 10월 1일
0 0 1 JAN,APR,JUL,OCT *  # 동일하지만 이름으로 표기
0 0 28-31 * *      # 말일 후보 — 날짜 체크 래퍼와 함께 사용

말일은 POSIX cron 표현식 자체로는 적을 수 없습니다. date -d tomorrow +%d = 01을 검사하는 래퍼를 돌리거나, 네이티브 지원이 있는 스케줄러를 쓰세요(Quartz는 L이 있고, Kubernetes는 없습니다).

연 단위와 매크로 단축

0 0 1 1 *          # 1월 1일 자정 (= @yearly / @annually)
0 0 25 12 *        # 크리스마스 자정
@yearly            # = 0 0 1 1 *
@monthly           # = 0 0 1 * *
@weekly            # = 0 0 * * 0
@daily             # = 0 0 * * *
@hourly            # = 0 * * * *
@reboot            # 특수: 데몬 시작 시 한 번 (vixie cron 전용)

이 표현식들은 모두 Crontab 생성기에 붙여 넣으면 다음 다섯 번 실행 시각이 미리 표시됩니다. 배포 전에 비용이 가장 적게 드는 사전 점검입니다.

Cron vs systemd 타이머 vs 클라우드 스케줄러 의사결정 매트릭스

Cron이 기본 선택이긴 하지만 항상 최선은 아닙니다. 아래는 가장 흔히 쓰이는 일곱 스케줄러를 비교한 표입니다. cron vs systemd timer를 결정할 때, Kubernetes CronJob과 Vercel cron job 사이에서 고민할 때, crontab에서 매니지드 클라우드로 옮길 때 유용합니다.

기능vixie cronsystemd 타이머K8s CronJobGHA scheduleAWS EventBridgeVercel CronCloudflare Workers
필드 문법5-필드 POSIXOnCalendar 스펙5-필드 POSIX + timeZone5-필드 POSIX6-필드 Quartz (? 사용)5-필드 POSIX5-필드 POSIX
최소 간격1분1초1분베스트 에포트, 15분 이상 권장1분1분 (Pro 플랜)1분
명시적 타임존CRON_TZ=Persistent=truespec.timeZone (1.27+)UTC 전용ScheduleExpressionTimezoneUTC 전용UTC 전용
누락 실행 복구없음 (anacron 사용)있음 (Persistent=true)있음 (startingDeadlineSeconds)없음있음없음없음
재시도 / 백오프없음부분적있음 (backoffLimit)실패 시 재시도있음없음있음
동시 실행 제어없음 (flock 사용)부분적있음 (concurrencyPolicy)없음없음없음없음
@reboot 지원있음있음 (OnBootSec=)없음없음없음없음없음

systemd 타이머가 cron보다 나은 경우

systemd 기반 Linux에서는 타이머가 진지한 대안입니다. 읽기 쉬운 캘린더 문법, 저널 통합, 누락 실행 복구를 갖췄습니다. 타이머와 짝이 되는 서비스 예시:

# daily-report.timer
[Unit]
Description=Run daily report at 9 AM
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
Unit=daily-report.service
[Install]
WantedBy=timers.target
# daily-report.service
[Unit]
Description=Daily report job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-report.sh
User=reporter

systemctl enable --now daily-report.timer로 활성화합니다. 결정적인 기능은 Persistent=true입니다. 머신이 오전 9시에 꺼져 있었더라도 부팅 직후 타이머가 실행됩니다. vixie cron에는 anacron 없이는 같은 기능이 없습니다. 서비스 하드닝은 보안 모범 사례를 참고하세요.

Kubernetes CronJob

Kubernetes는 POSIX 스케줄을 동시 실행 제어, 이력, 명시적 타임존 같은 원시 요소로 감쌉니다:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-report
spec:
  schedule: "0 2 * * *"
  timeZone: "America/New_York"     # Kubernetes 1.27+
  concurrencyPolicy: Forbid        # 동시에 두 개 실행 금지
  startingDeadlineSeconds: 300     # 5분 이상 지연되면 건너뜀
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: reporter
              image: reporter:1.4.0
              command: ["/usr/local/bin/report.sh"]

concurrencyPolicy: Forbidflock에 해당하는 장치입니다. 이 설정이 없으면 오래 도는 작업이 다음 실행 위로 쌓입니다. 모든 조절 가능 항목은 필드 참조 섹션을 참고하세요.

GitHub Actions schedule의 함정

GitHub Actions는 표준 5-필드 POSIX cron을 받아들입니다:

on:
  schedule:
    - cron: '0 9 * * 1-5'   # 평일 오전 9시 UTC

베스트 에포트 동작입니다. GitHub 러너 부하가 높을 때는 작업이 몇 분 늦게 실행되거나 아예 건너뛸 수 있습니다. 15분보다 짧은 간격은 피하세요. 타임존 설정은 없고 항상 UTC입니다.

AWS EventBridge의 Quartz 스타일 6-필드

AWS EventBridge는 Quartz 풍의 cron을 쓰는데 여섯 필드이며 두 날짜 슬롯 중 하나에 ?가 반드시 들어가야 합니다:

cron(0 9 * * ? *)

필드 순서: Minutes Hours Day-of-month Month Day-of-week Year. 한쪽 날짜 필드가 제한되어 있을 때 다른 쪽은 반드시 ?여야 합니다(POSIX의 OR 모호성을 Quartz가 해결하는 방식). Linux crontab을 그대로 복사하면 유효성 검사를 통과하지 못합니다.

Vercel Cron, Cloudflare Workers, Render Cron Jobs

새로운 서버리스 플랫폼은 5-필드 POSIX로 통일했습니다. Vercel cron job은 vercel.json{ "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] } 형태로 들어갑니다. Cloudflare Workers Cron Triggers는 wrangler.toml을 씁니다:

[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]

Render는 render.yaml을 씁니다. 셋 모두 UTC로 돌아가고 스케줄별 타임존 오버라이드는 없으므로, 처음부터 UTC로 설계하세요.

Cron 디버깅 함정 7가지 (그리고 잡아내는 방법)

“내 cron 작업이 안 돌아간다”는 신고는 대부분 일곱 가지 근본 원인 중 하나입니다. 스케줄러를 탓하기 전에 이 목록을 먼저 훑어보세요.

함정 1: PATH가 최소화되어 있다

Cron은 최소화된 $PATH(보통 /usr/bin:/bin)로 작업을 시작합니다. 인터랙티브 셸에는 /usr/local/bin, ~/.cargo/bin, .bashrc에 적힌 수십 가지 항목이 있지만 cron에는 아무것도 없습니다. cron debugging path environment에서 가장 흔한 문제가 이것입니다.

증상: node: command not found. 해결: crontab 최상단에 PATH를 설정하거나 절대 경로를 사용하세요.

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin

*/15 * * * *  /usr/local/bin/poll-api.sh
0 9 * * *     /home/deploy/.cargo/bin/my-rust-cli

함정 2: stdout과 stderr이 조용히 사라진다

기본적으로 cron 출력은 아무도 읽지 않는 메일 스풀로 갑니다. 작업은 조용히 실패합니다. 두 스트림을 모두 리다이렉트하세요:

*/15 * * * *  /usr/local/bin/job.sh >> /var/log/job.log 2>&1

JSON 출력은 jq에 파이프로 넣고, 로그 라인 추출은 정규식 치트시트를 참고하세요. systemd 타이머는 journalctl -u your-timer.service로 출력을 잡아낼 수 있습니다.

함정 3: 개발 환경과 운영 환경의 타임존 어긋남

뉴욕에 있는 노트북에서 0 9 * * *을 작성하고 미국 동부 시간 오전 9시를 기대했지만, 서버는 UTC로 돕니다. cron은 UTC 오전 9시(동부 시간 오전 4시)에 실행되어 아무도 모르는 사이 끝납니다. 해결: 서버를 UTC로 설정하고 일정도 UTC로 작성하거나, 타임존을 명시적으로 고정하세요.

CRON_TZ=America/New_York
0 9 * * *  /usr/local/bin/morning-report.sh

CRON_TZ는 vixie cron 3.0+에서 동작합니다. Kubernetes 1.27+는 spec.timeZone을, AWS EventBridge는 ScheduleExpressionTimezone을 지원합니다. GitHub Actions는 항상 UTC입니다. UTC와 DST, 에폭 계산은 Unix 타임스탬프 가이드를 참고하세요.

함정 4: 명령에서 이스케이프되지 않은 %

Cron은 이스케이프되지 않은 %를 줄바꿈으로 처리하며, 그 뒤의 내용은 명령의 stdin으로 들어갑니다. 그래서 date +"%Y-%m-%d"가 깨집니다. 모든 %\%로 이스케이프하거나, 로직을 스크립트로 옮기세요:

0 0 * * *  echo "Run at $(date +"\%Y-\%m-\%d")" >> /tmp/log

함정 5: 실행이 겹친다

*/5 * * * * 작업이 가끔 7분이 걸린다면 이전 인스턴스가 끝나기 전에 다음 인스턴스가 시작됩니다. 같은 행, 같은 락 파일, 같은 API 쿼터를 두고 두 복제본이 다툽니다. flock으로 직렬화하세요:

*/5 * * * *  flock -n /tmp/job.lock /usr/local/bin/job.sh

-n은 락이 잡혀 있으면 즉시 종료합니다. Kubernetes에서는 concurrencyPolicy: Forbid를 설정하세요. 락 파일의 권한도 중요한데, 자세한 내용은 보안 모범 사례를 참고하세요.

함정 6: 컨테이너 안의 @reboot

@reboot은 cron 데몬이 시작될 때 한 번 실행됩니다. VM에서는 그게 부팅 시점에 해당합니다. 컨테이너에서는 cron 데몬이 보통 PID 1이 아니거나 아예 실행되지 않을 수도 있습니다. 컨테이너에서는 @reboot을 사용하지 마십시오. 부팅 시 한 번 실행할 로직은 엔트리포인트나 init 컨테이너에 두세요.

함정 7: POSIX의 일/요일 OR 의미

가장 비싼 cron 함정입니다. POSIX 규칙: 일과 요일이 모두 제한되어 있을 때(둘 다 *가 아닐 때) 스케줄은 둘 중 하나가 일치하면 실행됩니다.

0 0 1 * 5는 “1일 자정, 금요일에만”처럼 보이지만 실제로는 1일과 매주 금요일에 모두 실행됩니다. 월에 따라 6~10번 더 실행되는 셈입니다.

# WRONG: looks like "1st of the month, only if Friday"
0 0 1 * 5
# RIGHT: pick one constraint
0 0 1 * *          # every 1st of the month
0 0 * * 5          # every Friday
# AND semantics need a wrapper
0 0 1-7 * 5  [ "$(date +\%u)" = "5" ] && /script    # first Friday only

의심 가는 표현식을 Crontab 생성기에 붙여 넣으면 다음 실행 미리보기로 OR 함정이 한눈에 드러납니다.

현대 스케줄러, Cron을 쓰지 말아야 할 때

Cron은 “이 명령을 정해진 주기에 대략 이 시각에 실행”하는 용도에 맞습니다. 그 옆에 붙어 있는 다음 문제들에는 잘 맞지 않습니다:

  • 의존성이 있는 워크플로(A 실행 후, A가 성공하면 B) → Airflow, Prefect, Dagster.
  • 재시도, 지수 백오프, 데드레터 큐Temporal, AWS Step Functions, Sidekiq.
  • 1분 미만 간격 → 반복 사이에 sleep하는 장기 실행 워커.
  • 초 단위 정밀 타이밍 → 전용 데몬. 매니지드 스케줄러는 정확한 타이밍을 보장하지 않습니다.
  • 이벤트 기반 작업 → 웹훅, 메시지 큐, change-data-capture 스트림.

그렇다고 cron이 사라지지는 않습니다. Airflow, Step Functions, Sidekiq 모두 워크플로의 진입점으로 cron 표현식을 받아들입니다. 다섯 필드 문법은 어디에나 재사용됩니다.

Kubernetes CronJob 필드 레퍼런스

위 의사결정 매트릭스에서는 최소 구성의 CronJob을 보였습니다. kubernetes cronjob syntax의 전체 필드 레퍼런스는 다음과 같습니다:

필드기본값역할
schedule필수POSIX 5-필드 cron 표현식
timeZone컨트롤러 TZ명시적 타임존 (1.27+); IANA 이름 사용
concurrencyPolicyAllowForbid는 이전 실행 중이면 새 실행을 건너뜀; Replace는 이전을 취소함
startingDeadlineSeconds무제한이 시간보다 더 지연되면 건너뜀
successfulJobsHistoryLimit3보존할 성공 Job 수
failedJobsHistoryLimit1보존할 실패 Job 수
suspendfalse삭제 없이 일시 정지
backoffLimit6Job이 Failed로 표시되기 전 Pod 재시도 횟수
activeDeadlineSeconds미설정Pod 런타임 상한
ttlSecondsAfterFinished미설정이 초가 지나면 Job을 자동 삭제

흔한 함정 두 가지가 있습니다. timeZone을 잊으면 스케줄이 kube-controller-manager 호스트의 타임존을 따라가게 됩니다(매니지드 Kubernetes에서는 예측 불가). 1분 주기 스케줄에서는 기본값 successfulJobsHistoryLimit: 3 때문에 ttlSecondsAfterFinished를 설정하지 않는 한 분당 세 개의 Job 객체가 쌓입니다.

크로스 플랫폼 Cron 대응

macOS launchd. Apple은 cron보다 launchd를 권장합니다. launchd 작업은 ~/Library/LaunchAgents/ 아래의 .plist입니다:

<plist version="1.0"><dict>
  <key>Label</key><string>com.example.daily</string>
  <key>ProgramArguments</key><array><string>/usr/local/bin/daily.sh</string></array>
  <key>StartCalendarInterval</key>
  <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>

launchctl load ~/Library/LaunchAgents/com.example.daily.plist로 로드합니다. cron과 달리 launchd는 sleep/wake 이후 누락된 실행을 따라잡아 줍니다.

Windows Task Schedulerschtasks를 씁니다:

schtasks /create /tn "DailyReport" /tr "C:\scripts\report.bat" /sc DAILY /st 09:00
schtasks /create /tn "EveryFifteen" /tr "C:\scripts\poll.bat" /sc MINUTE /mo 15

WSL에서는 네이티브 Linux cron이 동작하지만 세션이 끝나면 멈춥니다. 상시 켜져 있어야 하는 WSL 작업은 Task Scheduler로 띄우세요.

Docker 컨테이너 안의 Cron. 슬림 이미지 대부분(alpine, debian-slim, distroless)은 cron 데몬 없이 배포됩니다. croniebusybox-cron을 설치하고 tini 또는 s6-overlay로 PID 1로 띄우는 방식이 가능하긴 하나, 거의 항상 Kubernetes CronJob을 쓰는 편이 낫습니다.

고급 팁과 패턴

말일

Cron에는 “말일”을 표현하는 네이티브 연산자가 없습니다. 28-31 구간의 매일을 실행하면서 내일이 1일인지 검사하세요:

0 23 28-31 * *  [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh

월 N번째 요일

“첫째 주 월요일”은 같은 래퍼 패턴을 씁니다. 1-7일로 제한한 뒤 요일을 검사하면 됩니다:

0 9 1-7 * *  [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh

“마지막 금요일”은 25-31일 범위에 같은 요일 검사를 더해 만들 수 있습니다.

부하를 분산하는 랜덤 오프셋

같은 cron을 여러 머신이 돌릴 때 0 0 * * *은 UTC 자정에 thundering herd를 일으킵니다. 랜덤 지연을 뿌리세요:

RANDOM_DELAY=10                                              # cronie / anacron, 분 단위
0 0 * * *  /usr/local/bin/job.sh

0 0 * * *  sleep $((RANDOM \% 600)); /usr/local/bin/job.sh   # 이식 가능

하트비트 모니터링

Cron은 조용히 실패합니다. dead-man’s-switch 패턴: 작업이 성공적으로 끝날 때마다 모니터링 서비스에 핑을 보내고, 서비스는 예상한 핑이 도착하지 않을 때 경고를 띄웁니다. Healthchecks.io, Cronitor, Dead Man’s Snitch가 무료 등급을 제공합니다.

*/15 * * * *  /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid

응답 코드에 따라 분기하는 모니터링 로직(200은 정상, 429는 레이트 제한, 503은 성능 저하 등)은 HTTP 상태 코드 치트시트를 참고하세요.

멱등성은 스케줄러가 아니라 작업의 속성이다

Cron에는 재시도도, 누락 실행 복구도, 동시 실행 제어도 없습니다. 가장 믿을 만한 해법은 작업 자체를 여러 번 실행해도 안전하게 만드는 것입니다. “오전 9시에 오늘의 보고서를 보낸다”가 아니라 “오늘의 보고서가 아직 발송되지 않았다면 보낸다”로 설계하면, 누락 실행과 중복, 수동 재실행이 모두 같은 상태로 수렴합니다.

FAQ

*/5 * * * *은 정말 5분마다 실행되나요?

거의 그렇습니다 — */5 * * * *은 “지금부터 5분마다”가 아니라 분 0을 기준으로 고정됩니다. 매시 분 0, 5, 10, …, 55에 실행됩니다. 스텝 */N은 해당 필드의 최솟값을 기준으로 잡고 현재 시각은 보지 않습니다. 12:03에 저장하면 다음 실행은 12:08이 아니라 12:05입니다.

cron에서 0 0 * * *은 무슨 뜻인가요?

0 0 * * *은 서버의 로컬 타임존 기준 매일 자정(00:00)을 의미합니다. 필드는 분 0, 시 0, 임의의 일, 임의의 월, 임의의 요일을 의미합니다. 매크로 @daily 또는 @midnight과 동등합니다. 타임존을 고정하려면 crontab 최상단에 CRON_TZ=America/New_York을 추가하세요.

cron 작업을 30초마다 실행할 수 있나요?

표준 POSIX cron으로는 안 됩니다. 최소 단위가 1분이기 때문입니다. 우회 방법은 보통 세 가지입니다. * * * * *에 두 작업을 두고 한쪽에 sleep 30 &&을 붙여 시차를 주거나, OnCalendar=*:*:0/30으로 설정한 systemd 타이머를 쓰거나, 반복 사이에 sleep하는 장기 실행 워커를 두는 방식입니다. 마지막 방식이 보통 정답입니다.

cron은 기본적으로 어떤 타임존을 사용하나요?

서버의 로컬 시스템 타임존(/etc/timezone 또는 TZ 환경 변수)을 씁니다. UTC 서버에서 오전 9시 cron은 미국 동부 시간 새벽 4시에 실행됩니다. 해결 방법은 두 가지로, crontab 최상단에 CRON_TZ=를 설정하거나, 서버를 UTC로 두고 스케줄도 UTC로 설계하면 됩니다. GitHub Actions는 항상 UTC이고, Kubernetes 1.27+는 spec.timeZone을 지원합니다.

제 cron 작업이 왜 실행되지 않나요?

cron 작업이 실행되지 않는다면, 순서대로 확인하세요: cron 데몬이 돌고 있는가(systemctl status cron), crontab에 $PATH가 설정돼 있는가, stderr이 캡처되고 있는가(>> log 2>&1), 사용자의 crontab이 로드돼 있는가(crontab -l), 명령에서 %가 이스케이프돼 있는가, 타임존이 예상과 같은가. “실행되지 않는다”는 신고는 대부분 두 번째나 세 번째 항목입니다.

Kubernetes CronJob 문법은 Linux cron과 같은가요?

schedule 필드만 놓고 보면 같습니다. 둘 다 POSIX 5-필드 cron을 씁니다. Kubernetes는 그 위에 spec.timeZone(1.27+), 중첩 제어용 concurrencyPolicy, 누락 실행 복구용 startingDeadlineSeconds, 일시 정지용 suspend: true를 더합니다. Linux cron에는 이런 기능이 없으므로 flockanacron으로 대신해야 합니다.

@reboot@daily의 차이는 무엇인가요?

@daily0 0 * * *의 매크로이며, 매일 자정에 고정 스케줄로 실행됩니다. @reboot은 cron 데몬이 시작될 때 한 번만 실행되며 반복 스케줄이 없습니다. @reboot은 vixie cron과 cronie가 지원하지만 Kubernetes CronJob, GitHub Actions, AWS EventBridge는 지원하지 않습니다. 컨테이너에서는 @reboot이 거의 실행되지 않습니다.

cron과 crontab의 차이는 무엇입니까?

cron은 예약된 작업을 실행하는 백그라운드 데몬이고, crontab은 그 작업들을 나열하는 파일입니다(파일을 편집하는 crontab 명령도 포함). 데몬은 각 사용자의 crontab을 주기적으로 읽어 실행 시각이 cron 표현식과 일치하는 명령을 실행합니다. 요컨대 cron이 엔진이고, crontab이 레시피입니다.