crontab 실행 안 됨? cron 작업 안 돌아갈 때 원인 7가지와 5분 진단법
분명히 crontab -e로 등록했고, 문법도 맞는 것 같은데 예약한 시각이 지나도 아무 일이 일어나지 않습니다. 더 답답한 건 터미널에서 스크립트를 직접 실행하면 멀쩡히 잘 돌아간다는 점이죠. 이 글에 들어왔다면 십중팔구 그 상황일 겁니다.
cron의 가장 고약한 특징은 조용히 실패한다는 것입니다. 에러를 화면에 띄워주지 않고, 별도로 출력을 잡아두지 않으면 무슨 일이 일어났는지조차 알 수 없습니다. 그래서 디버깅은 "추측"이 아니라 "로그를 보이게 만드는 것"에서 시작해야 합니다. 이 글은 증상 → 원인 → 해결 순서로, 복붙 가능한 명령어와 함께 5분 안에 원인을 좁히도록 안내합니다. (Ubuntu 22.04/24.04, RHEL 9 기준)
가장 먼저 할 일: cron 데몬과 로그부터 확인하기
코드를 의심하기 전에, cron 데몬이 살아있는지부터 확인하세요. 데몬이 죽어 있으면 어떤 완벽한 crontab도 실행되지 않습니다.
# Ubuntu / Debian
systemctl status cron
# RHEL / CentOS / Rocky / Alma
systemctl status crondactive (running)이 아니라면 켜고, 부팅 시 자동 시작까지 등록합니다.
sudo systemctl enable --now cron # Ubuntu/Debian
sudo systemctl enable --now crond # RHEL 계열데몬이 살아있다면 다음은 로그입니다. cron은 "작업을 실행했다"는 기록을 남깁니다. 이 기록이 있으면 cron은 제 역할을 한 것이고, 문제는 스크립트 안에 있습니다. 기록조차 없으면 cron이 작업을 아예 인식하지 못한 것이죠.
# Ubuntu/Debian (syslog 사용)
grep CRON /var/log/syslog
# rsyslog가 없는 최신 환경 / 공통
journalctl -u cron --since "10 min ago" # Ubuntu
journalctl -u crond --since "10 min ago" # RHEL
# RHEL 계열 전용 로그
tail -f /var/log/cronUbuntu 24.04나 최소 설치 환경에서는
rsyslog가 빠져 있어/var/log/syslog가 없을 수 있습니다. 이때는journalctl로 봐야 로그가 보입니다.
마지막으로, 실패 원인을 눈에 보이게 만드는 가장 강력한 한 줄을 소개합니다. 디버깅 중인 작업 끝에 출력 리다이렉트를 붙이세요.
* * * * * /path/to/script.sh >> /tmp/cron.log 2>&1>>로 표준 출력을, 2>&1로 표준 에러까지 같은 파일에 모읍니다. 1분 뒤 cat /tmp/cron.log를 열면 command not found, Permission denied 같은 진짜 원인이 그대로 찍혀 있습니다.
실행 자체가 안 되는 환경 문제
로그에 작업은 찍히는데 결과물이 없다면, 대부분 cron의 실행 환경이 내 터미널과 다르기 때문입니다.
원인 1·2: PATH 환경변수 차이 (command not found)
이게 cron 트러블슈팅의 80%입니다. cron은 로그인 셸이 아니라 극도로 빈약한 환경에서 작업을 실행합니다. 직접 확인해 보세요.
* * * * * env > /tmp/cronenv.txt1분 뒤 cat /tmp/cronenv.txt를 보면, 평소 터미널의 echo $PATH와 전혀 다릅니다. cron의 PATH는 보통 /usr/bin:/bin뿐이라, /usr/local/bin/에 있는 node, python3, docker 같은 명령을 못 찾습니다.
해결책은 두 가지이고, 둘 다 적용하는 것을 권합니다.
# 1) crontab 상단에 PATH 명시
PATH=/usr/local/bin:/usr/bin:/bin
# 2) 명령은 절대경로로 (which python3 로 확인)
* * * * * /usr/bin/python3 /home/app/job.py >> /tmp/cron.log 2>&1which python3로 실제 경로를 확인한 뒤 절대경로로 박아두면 가장 확실합니다.
원인 5: 사용자 crontab vs /etc/crontab 혼동
crontab -e로 등록하는 사용자 crontab과 /etc/crontab(시스템 crontab)은 문법이 다릅니다. 가장 큰 차이는 사용자 컬럼입니다.
| 구분 | 등록 방법 | 시간 필드 뒤 사용자 컬럼 |
|---|---|---|
| 사용자 crontab | crontab -e | 없음 (해당 유저로 실행) |
| 시스템 crontab | /etc/crontab, /etc/cron.d/* | 있음 (root 등) |
# 사용자 crontab (crontab -e)
* * * * * /home/app/job.sh
# /etc/crontab — 5번째 뒤에 user 컬럼이 필요!
* * * * * root /home/app/job.sh/etc/crontab에 사용자 컬럼 없이 작성하면 한 칸씩 밀려 통째로 무시됩니다. 반대로 crontab -e에 root를 넣으면 그게 명령어로 해석돼 깨집니다.
등록은 됐는데 무시되거나 깨지는 문법·권한 문제
원인 3: % 문자 미이스케이프
cron에서 %는 개행(줄바꿈)으로 해석되는 특수문자입니다. date 포맷에 %가 들어가면 그 뒤가 잘려나가 명령이 깨집니다.
# Before (실패) — %Y 뒤가 잘림
* * * * * /usr/bin/backup.sh > /tmp/dump_$(date +%Y%m%d).sql
# After (정상) — % 앞에 백슬래시
* * * * * /usr/bin/backup.sh > /tmp/dump_$(date +\%Y\%m\%d).sql원인 4: 마지막 줄 개행 누락
crontab 파일의 마지막 줄에 개행이 없으면 그 줄은 통째로 무시됩니다. 에디터로 직접 만지거나 echo로 추가했다면 의심해 보세요. crontab -e로 열어 맨 끝에서 엔터를 한 번 쳐주는 것만으로 해결됩니다.
원인 6: 실행권한·shebang 누락
cron은 스크립트를 호출만 합니다. 권한이나 인터프리터 선언이 빠지면 그냥 실패합니다.
# Before: 실행권한 없음 → Permission denied
-rw-r--r-- 1 app app 320 job.sh
# After
chmod +x /home/app/job.sh스크립트 첫 줄 shebang도 필수입니다.
#!/bin/bash # ← 이 한 줄이 없으면 어떤 셸로 실행할지 몰라 깨질 수 있음
echo "작업 시작"원인 7: 출력 미리다이렉트라는 함정
리다이렉트를 안 걸면 에러가 어디에도 안 남고, "왜 안 되지?"를 무한 반복하게 됩니다. **디버깅 단계에서는 무조건 >> /tmp/cron.log 2>&1**을 붙이세요. 이게 7가지 원인 중 가장 흔한 시간 낭비 요인입니다.
실무 한마디 — 제가 가장 많이 본 사고는 "로컬에선
python job.py로 잘 됐는데 cron만 안 되더라"였습니다. 원인은 거의 항상 PATH와 **작업 디렉터리(cwd)**였습니다. cron은 해당 유저의 홈 디렉터리에서 실행되므로, 스크립트가 상대경로로 파일을 읽으면 못 찾습니다. 스크립트 맨 위에cd /home/app/project한 줄을 박아두는 습관만으로 절반은 예방됩니다.
결론: 실행 안 될 때 '5분 진단 순서' 체크리스트
순서대로만 따라가면 원인이 좁혀집니다.
- 데몬 살아있나 →
systemctl status cron(또는crond) - 로그에 작업이 찍히나 →
journalctl -u cron --since "10 min ago"/grep CRON /var/log/syslog- 안 찍힘 → 문법·등록 위치(
crontab -evs/etc/crontab)·마지막 줄 개행 의심 - 찍힘 → 스크립트 내부 문제로 이동
- 안 찍힘 → 문법·등록 위치(
- 절대경로/PATH 확인 →
env > /tmp/cronenv.txt로 cron 환경 비교, 명령은 절대경로 + 상단PATH= - 권한·shebang →
chmod +x, 첫 줄#!/bin/bash - 출력 리다이렉트로 에러 캡처 →
>> /tmp/cron.log 2>&1붙이고cat /tmp/cron.log
컨테이너·클라우드 환경이라면 cron 대신 systemd timer나 Kubernetes CronJob이 로그(journalctl)와 상태 관리 측면에서 더 깔끔합니다. 다만 단일 리눅스 서버에서는 여전히 crontab이 가장 빠르고 흔한 선택지죠. 재시도·실패 알림·정확한 로깅이 필요해지는 순간이 systemd timer로 옮길 시점입니다.
자주 묻는 질문 (FAQ)
Q. 수동 실행은 되는데 cron에서만 안 됩니다. 왜죠?
A. cron은 로그인 셸이 아니어서 PATH 등 환경변수가 빈약하고, 작업 디렉터리도 유저 홈으로 고정되며, 기본 셸이 /bin/sh일 수 있습니다. 절대경로 사용, crontab 상단 PATH= 지정, 스크립트에 cd 명시, shebang 추가로 대부분 해결됩니다.
Q. 분 단위(* * * * *)로 등록했는데 안 돕니다.
A. 우선 데몬 상태와 로그를 확인하세요. 로그에 CRON 기록조차 없다면 마지막 줄 개행 누락, % 미이스케이프, /etc/crontab의 사용자 컬럼 누락으로 라인이 무시됐을 가능성이 큽니다.
Q. root crontab과 user crontab은 뭐가 다른가요?
A. sudo crontab -e는 root 권한으로, crontab -e는 현재 사용자 권한으로 실행됩니다. 파일 접근 권한과 환경이 다르므로, 권한이 필요한 작업인데 user crontab에 등록했다면 Permission denied가 날 수 있습니다. 어느 유저로 등록했는지 crontab -l로 꼭 확인하세요.
Q. GUI나 SSH 세션의 환경변수가 cron에는 왜 없나요?
A. 그 변수들은 .bashrc·.profile 같은 로그인 셸 초기화 파일에서 설정되는데, cron은 이 파일들을 읽지 않기 때문입니다. 필요한 변수는 crontab 상단에 직접 정의하거나, 스크립트 안에서 source ~/.bashrc로 명시적으로 불러와야 합니다.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...