IT·리눅스/리눅스

cron vs systemd timer - 정기 작업, 어느 쪽을 써야 하나

jmineekim 2026. 6. 4. 10:00

새 서버 셋업하다가 "매일 새벽 3시에 백업 스크립트 한 번 돌려놔 주세요" 같은 요청을 받으면, 대부분 반사적으로 crontab -e를 칩니다. 빠르고 익숙하니까요. 그런데 막상 돌려보면 stdout 로그가 어디로 갔는지도 모르겠고, 실패하면 알림도 못 받고, 환경 변수가 안 먹어서 또 한참 헤맵니다. 요즘 대부분의 리눅스 배포판에는 systemd timer라는 대안이 같이 들어 있어요. 오늘은 두 방식의 차이를 정리하고, 같은 작업을 양쪽으로 어떻게 짜는지, 그리고 언제 어느 쪽을 쓰는 게 합리적인지 풀어 볼게요.

개념 / 원리

cron은 유닉스 초창기부터 있던 전통적인 스케줄러입니다. cron 데몬이 1분마다 깨어나서 /etc/crontab, /etc/cron.d/*, 각 유저의 crontab -e 항목을 읽고 시간에 맞는 명령을 실행합니다. 구조가 단순하고 문법(분 시 일 월 요일 명령)도 익숙해요.

systemd timer는 systemd 생태계의 스케줄러입니다. .timer 파일과 .service 파일이 한 쌍으로 동작해요. .timer가 "언제" 트리거할지 정의하고, 그 시점에 .service를 띄웁니다.

항목 cron systemd timer
문법 0 3 * * * OnCalendar=*-*-* 03:00:00
로그 /var/log/cron, 메일 journalctl -u <name>
환경 변수 거의 비어 있음 .service에 명시적으로 지정
종속성·의존성 없음 After=, Requires=로 표현
실패 시 재시도 없음 (직접 wrapper 필요) Restart=로 처리
누락된 실행 따라잡기 안 됨 Persistent=true 지원
도입 난이도 매우 낮음 (한 줄) 두 파일 작성 필요

cron은 단순한 작업에 무난하고, systemd timer는 운영 안정성을 챙기고 싶을 때 강합니다.

실전 예제

매일 새벽 3시에 /opt/scripts/backup.sh를 돌리는 같은 작업을 양쪽으로 짜 보겠습니다.

cron 버전:

# crontab -e (현재 유저)
# 또는 /etc/cron.d/backup 파일 생성 (시스템 전체)

# m  h  dom mon dow  command
   0  3   *   *   *  /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

저장하고 끝. 1분 안에 cron이 새 항목을 읽어 들입니다. 위에서 >> /var/log/backup.log 2>&1로 표준출력·에러를 직접 파일로 리다이렉트했는데, 안 하면 cron이 메일로 출력을 보내려고 시도하다가 실패하고 로그도 안 남습니다.

systemd timer 버전:

먼저 실제 작업을 정의하는 .service 파일.

# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backupuser

그리고 트리거 시점을 정의하는 .timer 파일.

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 03:00

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

활성화는 timer 쪽을 enable·start 합니다(service가 아니라).

sudo systemctl daemon-reload
sudo systemctl enable backup.timer
sudo systemctl start backup.timer

# 등록된 타이머 목록과 다음 실행 시각
systemctl list-timers --all

# 로그
journalctl -u backup.service -f

Persistent=true 한 줄이 중요합니다. 서버가 새벽 3시에 꺼져 있다가 5시에 켜졌다면, cron은 그냥 그 회차를 건너뛰지만 systemd timer는 "놓친 실행"을 부팅 직후에 한 번 따라 실행합니다.

OnCalendar 문법은 좀 더 풍부합니다. 몇 가지 예를 들면:

OnCalendar=hourly                 # 매시간 정각
OnCalendar=daily                  # 매일 자정
OnCalendar=Mon,Wed 09:00          # 월·수 9시
OnCalendar=*-*-* 03:00:00         # 매일 새벽 3시 (cron의 0 3 * * *)
OnCalendar=*-*-01 04:00:00        # 매월 1일 4시

주의할 점

1) cron의 환경 변수는 정말 빈약합니다. PATH/usr/bin:/bin 정도만 잡혀 있어서 python3 같은 명령이 못 찾고 실패할 수 있어요. 스크립트 안에서 /usr/local/bin/python3처럼 절대경로를 쓰거나, crontab 상단에 PATH=...를 명시합니다. systemd timer는 .serviceEnvironment=로 깔끔하게 잡을 수 있습니다.

2) cron 로그는 한 군데에 모이지 않습니다. stdout/stderr를 직접 파일로 리다이렉트하지 않으면 일부 배포판에서는 메일로 가거나, 그냥 사라집니다. systemd timer는 모든 출력이 journalctl로 모여서 journalctl -u backup.service로 한 번에 봅니다.

3) 실패 시 알림은 cron이 약합니다. cron은 실패 자체를 모릅니다. 종료 코드가 0이 아니어도 그냥 끝나요. systemd timer는 OnFailure=로 실패 시 다른 서비스를 띄울 수 있어서, 알림 서비스를 한 번 연결해 두면 운영이 훨씬 편합니다.

4) cron에 % 쓸 때 이스케이프 필요. crontab 안에서 %는 줄바꿈으로 해석돼서 그 뒤가 표준 입력으로 전달됩니다. date +%Y-%m-%d 같은 명령을 그냥 박으면 Y-%m-%d 부분이 입력으로 들어가서 이상하게 동작합니다. \% 로 이스케이프해야 해요. 작은 함정이지만 자주 만납니다.

5) systemd timer는 단순 작업에는 과합니다. 두 파일 만들어야 하고, daemon-reload 하고, enable 하고… 매일 1분짜리 cron job 추가에 굳이 이렇게 갈 필요는 없어요. 운영 중요도가 높아진다 싶을 때(실패 알림 필요, 로그 추적 필요, 종속성 있음) 전환을 고려하면 됩니다.

6) 둘 다 정확히 같은 시각에 안 도는 경우가 있습니다. cron은 분 단위, systemd timer는 기본 정확도가 1분 정도예요. 더 정밀하게 보장하고 싶으면 AccuracySec=1s 같이 조정할 수 있지만, 분 단위 작업에는 그럴 일이 거의 없습니다.

마무리

정리하면 세 줄입니다.

  • 빠르게 한 줄로 등록할 거면 cron, 운영 안정성(로그·알림·종속성)이 중요하면 systemd timer.
  • systemd timer의 결정적 장점은 journalctl 통합 로그와 Persistent=true로 놓친 실행 따라잡기.
  • cron 쓸 거면 PATH 명시 + stdout/stderr 리다이렉트 + 종료 코드 모니터링은 직접 챙기자.
반응형