가이드

Crontab 예제: 리눅스 작업 스케줄링 문법과 자주 하는 실수

업데이트: · Skillaria.Top

crontab은 cron 데몬이 명령을 실행하는 시간표입니다. 각 줄은 시간을 나타내는 다섯 개 필드(분, 시, 일, 월, 요일)와 명령으로 이루어집니다. 예를 들어 */5 * * * * /opt/backup.sh는 5분마다 스크립트를 실행합니다. 표는 crontab -e로 편집하며, 대부분의 문제는 cron의 제한된 환경 때문에 생기므로 절대 경로를 쓰고 출력을 로그 파일로 보내야 합니다.

Cron은 리눅스의 표준 작업 스케줄러입니다. cron 데몬은 1분마다 시간표(crontab)를 읽고, 실행 시각이 된 명령을 시작합니다. 야간 백업, 임시 파일 정리, 인증서 갱신, 보고서 발송 같은 작업이 여기서 실행됩니다. 아래에서는 문법, 자주 쓰는 crontab 예제, 그리고 작업이 "조용히" 실행되지 않는 이유를 다룹니다.

crontab 열기와 편집

각 사용자는 자신만의 시간표를 가집니다. 파일을 /var/spool/cron에서 직접 고치지 말고 crontab 명령으로 다루세요.

명령어동작
crontab -e자신의 시간표를 편집기에서 열기 (처음 실행하면 편집기를 고르라고 묻습니다)
crontab -l현재 시간표 보기
crontab -r시간표 전체 삭제, 확인 없음
crontab -i -r확인을 묻고 삭제 (데비안/우분투, RHEL)
sudo crontab -u alice -e다른 사용자의 시간표 편집
crontab 파일시간표를 파일 내용으로 교체

crontab -e로 저장하면 문법을 검사하고 crontab: installing new crontab을 출력합니다. 줄에 오류가 있으면 errors in crontab file, can't install이 나오고 다시 편집하라고 안내합니다. 데몬을 재시작할 필요는 없습니다. 변경 사항은 자동으로 반영됩니다.

-r 옵션은 조심하세요. e와 r 키가 가까이 있고, 백업이 없으면 시간표를 되돌릴 방법이 없습니다. 백업을 습관으로 들이는 것이 좋습니다.

crontab -l > ~/crontab.bak
# 복원
crontab ~/crontab.bak

crontab 문법: 다섯 개의 시간 필드

작업 한 줄은 다음 형식입니다.

# ┌───────── 분 (0–59)
# │ ┌─────── 시 (0–23)
# │ │ ┌───── 일 (1–31)
# │ │ │ ┌─── 월 (1–12 또는 jan–dec)
# │ │ │ │ ┌─ 요일 (0–7 또는 sun–sat; 0과 7은 일요일)
# │ │ │ │ │
  * * * * * 명령

각 필드에는 다음 특수 문자를 쓸 수 있습니다.

문자의미예시
*모든 값* * * * * — 매분
,목록0 9,18 * * * — 9시와 18시
-범위0 9-18 * * * — 9시부터 18시까지 매시 정각
/간격*/15 * * * * — 15분마다

간격과 범위는 함께 쓸 수 있습니다. 0 8-20/2 * * *은 8시부터 20시까지 2시간마다(8, 10, …, 20시)입니다. 시간은 시스템의 시간대를 따르므로 timedatectl로 확인하세요.

매일 쓰는 crontab 예제

일정crontab 줄
매분* * * * *
5분마다*/5 * * * *
30분마다*/30 * * * * 또는 0,30 * * * *
매시 정각0 * * * *
6시간마다0 */6 * * *
매일 3:000 3 * * *
매일 23:4545 23 * * *
평일 9:000 9 * * 1-5
주말 정오0 12 * * 6,0
월요일 8:3030 8 * * 1
매월 1일 0:000 0 1 * *
분기마다0 0 1 1,4,7,10 *
매년 1월 1일0 0 1 1 *
근무 시간에 10분마다*/10 9-18 * * 1-5

완전한 작업 줄은 일정과 명령을 합친 형태입니다.

# 매일 밤 3:00에 데이터베이스 백업
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1

# 일요일마다 7일보다 오래된 임시 파일 정리
0 4 * * 0 /usr/bin/find /tmp/app -type f -mtime +7 -delete

# 5분마다 사이트 상태 확인
*/5 * * * * /usr/bin/curl -fsS https://example.com/health > /dev/null || /usr/local/bin/alert.sh

@reboot, @daily 같은 특수 문자열

다섯 개의 필드 대신 짧은 별칭을 쓸 수 있습니다.

별칭해당 표현
@rebootcron 데몬이 시작될 때 한 번 (시스템 부팅 후)
@hourly0 * * * *
@daily / @midnight0 0 * * *
@weekly0 0 * * 0
@monthly0 0 1 * *
@yearly / @annually0 0 1 1 *
@reboot /home/alice/bin/start-tunnel.sh >> /home/alice/tunnel.log 2>&1

계속 실행되어야 하고 죽으면 재시작되어야 하는 서비스에는 @reboot가 약한 해결책입니다. 이런 경우에는 Restart=on-failure가 설정된 systemd 유닛을 만드는 것이 좋습니다.

cron으로 N초마다 실행하기

cron의 최소 단위는 1분입니다. 더 자주 실행해야 한다면 두 가지 방법이 있습니다. 첫째는 sleep으로 시간을 어긋나게 한 줄을 추가하는 것입니다.

* * * * * /opt/check.sh
* * * * * sleep 30; /opt/check.sh

둘째는 더 올바른 방법으로, OnUnitActiveSec=30s를 쓰는 systemd 타이머입니다. 1분보다 자주 실행해야 한다면 대개 cron이 아니라 데몬이나 타이머가 맞는 도구입니다.

시스템 crontab: /etc/crontab과 /etc/cron.d

사용자 시간표 외에 시스템 시간표도 있습니다. 형식이 조금 다릅니다. 시간과 명령 사이에 명령을 실행할 사용자 이름이 들어갑니다.

# /etc/cron.d/certbot-renew
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root certbot renew --quiet

위치별 역할은 다음과 같습니다.

  • /etc/crontab — 기본 시스템 시간표입니다. 데비안과 우분투에서는 아래 디렉터리들이 여기서 실행됩니다.
  • /etc/cron.d/ — 작업을 담은 개별 파일입니다. 패키지로 설치하거나 Ansible로 배포하기 편합니다.
  • /etc/cron.hourly/, cron.daily/, cron.weekly/, cron.monthly/ — 일정 없이 실행 파일 스크립트만 넣습니다. 이들은 run-parts(또는 anacron)가 실행합니다.

run-parts의 함정: 데비안과 우분투에서는 이름에 점(.)이 들어간 파일을 건너뜁니다. /etc/cron.daily/의 backup.sh는 실행되지 않으므로 이름을 backup으로 바꾸세요. 실행될 목록은 run-parts --test /etc/cron.daily로 확인할 수 있습니다.

/etc/cron.d/의 파일은 root 소유여야 하고, 그룹과 다른 사용자가 쓸 수 없어야 하며, 마지막에 줄바꿈이 있어야 합니다. 그렇지 않으면 cron이 이 파일을 무시합니다.

cron 환경: PATH, SHELL, 변수

"터미널에서는 되는데 cron에서는 안 된다"는 말의 주된 원인은 환경입니다. cron은 명령을 /bin/sh로, .bashrc나 .profile을 읽지 않고, 최소한의 PATH(보통 /usr/bin:/bin)로 실행합니다. 그래서 /usr/local/bin에 있는 docker, node, php를 찾지 못할 수 있습니다.

변수는 시간표 맨 위에서 정의할 수 있습니다.

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
CRON_TZ=Europe/Moscow

0 6 * * * cd /srv/app && ./report.sh
  • MAILTO — 작업 출력을 보낼 주소입니다. MAILTO=""는 메일 발송을 끕니다.
  • CRON_TZ — 일정에 쓸 시간대입니다(RHEL/Fedora의 cronie에서 지원하며, 데비안 버전의 cron에서는 동작하지 않습니다).
  • 변수 값에 $HOME/bin 같은 치환은 되지 않습니다. 경로를 전부 적으세요.

가장 확실한 방법은 작업에서 모든 프로그램과 파일을 절대 경로로 지정하고, 스크립트의 첫 줄에 #!/bin/bash를 적는 것입니다.

cron 로그 보기

Cron은 작업이 시작되었다는 사실은 기록하지만, 작업의 출력은 기록하지 않습니다.

# 데비안/우분투
grep CRON /var/log/syslog
# systemd-journald를 통해
journalctl -u cron --since today      # 데비안/우분투
journalctl -u crond --since today     # RHEL, Fedora, Rocky
# journald가 없는 RHEL
tail -f /var/log/cron

실행 기록은 보통 다음과 같이 나옵니다.

Oct 14 03:00:01 web1 CRON[21544]: (root) CMD (/usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1)

작업이 무엇을 출력했고 어디서 실패했는지 보려면 표준 출력과 표준 오류를 모두 파일로 보내세요: >> /var/log/job.log 2>&1. 이렇게 하지 않으면 출력은 로컬 메일로 전송되고, 메일 에이전트가 없으면 사라집니다(로그에 No MTA installed, discarding output이 기록됩니다).

cron이 작업을 실행하지 않는 흔한 이유

명령을 찾을 수 없음

증상: 작업 로그에 command not found가 나옵니다. 원인은 제한된 PATH입니다. 해결법은 프로그램의 절대 경로를 쓰는 것입니다(which docker로 경로를 알 수 있습니다). 또는 시간표 맨 위에 PATH= 줄을 추가하세요.

명령 안의 퍼센트 기호

crontab에서 % 기호는 줄바꿈을 뜻합니다. 첫 번째 % 뒤의 모든 내용은 표준 입력으로 명령에 전달됩니다. 그래서 파일 이름에 날짜를 넣는 흔한 줄이 깨집니다.

# 잘못된 예: 첫 번째 %에서 명령이 잘림
0 2 * * * tar czf /backup/site-$(date +%F).tar.gz /var/www
# 올바른 예: 이스케이프
0 2 * * * tar czf /backup/site-$(date +\%F).tar.gz /var/www

더 간단한 방법은 로직을 스크립트로 옮기는 것입니다. 스크립트 안에서는 %를 이스케이프할 필요가 없습니다.

스크립트에 실행 권한이 없거나 줄바꿈이 잘못됨

Permission denied는 파일에 실행(x) 권한이 없다는 뜻입니다. chmod +x script.sh를 실행하거나 /bin/bash /opt/script.sh처럼 인터프리터를 직접 지정하세요. 스크립트를 Windows에서 작성했다면 CRLF 줄바꿈 때문에 /bin/bash^M: bad interpreter 같은 이상한 오류가 납니다. sed -i 's/\r$//' script.sh 또는 dos2unix로 고치세요.

상대 경로와 작업 디렉터리

작업은 사용자의 홈 디렉터리에서 시작합니다. 스크립트가 ./config.ini를 읽는다면 ~에서 찾게 됩니다. 명령 앞에 cd /srv/app &&를 붙이거나, 스크립트 안에서 절대 경로를 쓰세요.

날짜와 요일을 동시에 지정

두 필드를 모두 지정하면 cron은 두 조건 중 하나만 맞아도 실행합니다. 둘 다 맞아야 하는 것이 아닙니다. 예를 들어 0 9 13 * 5는 13일이면서 금요일인 날이 아니라, 13일 그리고 모든 금요일에 실행됩니다. 원하는 조건은 명령 안에서 확인하세요.

0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/friday13.sh

작업이 서로 겹침

백업이 실행 간격보다 오래 걸리면, 다음 실행이 이전 실행 위에서 시작됩니다. flock으로 잠금을 걸어 막을 수 있습니다.

*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /opt/sync.sh

-n 옵션을 쓰면 이전 실행이 아직 돌고 있을 때 새 실행은 바로 종료됩니다.

cron 데몬이 실행 중이 아님

systemctl status cron으로 확인하세요(RHEL에서는 crond). 최소 이미지, 예를 들어 Docker 컨테이너에는 cron 데몬이 아예 없어서 crontab에 적은 작업을 실행할 주체가 없습니다.

cron 작업 디버깅

cron과 최대한 비슷한 환경에서 명령을 실행해 보세요.

env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=$HOME /bin/sh -c '/opt/backup.sh'

여기서 오류가 난다면 cron에서도 같은 오류가 납니다. 잠시 * * * * * 일정을 걸고, 출력을 파일로 보내고, tail -f로 지켜보세요. 일정 표현식을 검사하는 온라인 계산기도 있지만, 최종 판단은 시스템 로그가 합니다.

누가 cron을 쓸 수 있나: cron.allow와 cron.deny

접근은 /etc/cron.allow와 /etc/cron.deny 파일로 제한합니다. cron.allow가 있으면 그 안에 적힌 사용자만 crontab을 쓸 수 있습니다. 없고 cron.deny만 있으면, 거기 적힌 사용자를 제외한 모두가 쓸 수 있습니다. 금지된 사용자가 crontab -e를 실행하면 You (bob) are not allowed to use this program이 나옵니다.

정리

Crontab은 다섯 개의 시간 필드와 명령으로 이루어진 줄입니다. */5 * * * *는 5분마다, 0 3 * * *는 매일 밤, 0 9 * * 1-5는 평일 아침입니다. 시간표는 crontab -e로 편집하고, 절대 경로를 쓰고, %를 이스케이프하고, 출력은 로그 파일로 보내고, 오래 걸리는 작업은 flock으로 보호하세요. 이 정도면 대부분의 서버 작업을 자동화할 수 있습니다. 작업 자동화와 스케줄링은 리눅스 과정에서 실습으로 다룹니다.

자주 묻는 질문

cron과 anacron은 어떻게 다른가요?

Cron은 정확한 시각에 작업을 실행하고, 컴퓨터가 꺼져 있었다면 그 실행을 건너뜁니다. Anacron은 일간, 주간, 월간 작업이 부팅 후에 반드시 한 번 실행되도록 보장하므로 노트북이나 데스크톱에 적합합니다.

cron과 systemd 타이머 중 무엇을 써야 하나요?

Cron은 간단하고 거의 모든 시스템에 있습니다. systemd 타이머는 journalctl로 로그를 보고, 의존성을 지정하고, 자원을 제한하며, Persistent=true로 놓친 실행을 보충할 수 있어서, 중요한 작업에는 최신 서버에서 점점 더 많이 쓰입니다.

사용자의 crontab 파일은 실제로 어디에 있나요?

데비안과 우분투에서는 /var/spool/cron/crontabs/사용자이름, RHEL과 Fedora에서는 /var/spool/cron/사용자이름에 있습니다. 이 파일을 직접 고치지 말고 crontab -e를 사용하세요.

crontab의 작업을 일시적으로 끄려면 어떻게 하나요?

crontab -e로 해당 줄 앞에 #을 붙여 주석 처리하세요. 사용자의 모든 작업을 끄려면 crontab -l로 파일을 저장해 둔 뒤 crontab -r로 삭제합니다.

달의 마지막 날에 작업을 실행하려면 어떻게 하나요?

그런 전용 필드는 없습니다. 28일부터 31일까지 실행하고, 내일이 1일인지 확인하세요: 0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/job.sh.

영상으로 이 주제 배우기

이 주제는 “Linux: с нуля до уверенного администрирования” 강의에서 실습과 함께 자세히 다룹니다.

강의로 이동