Tình huống xảy ra
Bạn thiết lập một crontab entry, chờ đến giờ chạy, nhưng không có gì xảy ra. Không có output, không có log, không có lỗi. Khi kiểm tra /var/log/syslog (hoặc /var/log/cron trên RHEL/CentOS), bạn thấy dòng này:
CRON[12345]: (root) CMD (/opt/scripts/backup.sh)
CRON[12345]: (CRON) info (no MTA installed, discarding output)
Vấn đề ở đây là — job đã chạy. Cron đã thực thi nó. Nhưng mọi output mà script tạo ra đều bị bỏ đi âm thầm vì không có mail transfer agent nào được cài để gửi đi. Nếu script của bạn thoát với mã lỗi 1, bạn sẽ không bao giờ biết được.
Tại sao điều này xảy ra
Cron được thiết kế để gửi email output của job đến chủ sở hữu crontab. Trên một server Ubuntu hoặc CentOS cài đặt tối giản, không có MTA nào (như postfix, sendmail, hay ssmtp) — vì vậy cron ghi lại cảnh báo đó và bỏ output đi.
Trong môi trường production, đây là một vấn đề nghiêm trọng. Script backup của bạn có thể đang thất bại mỗi đêm lúc 2 giờ sáng, và bạn sẽ không có bất kỳ dấu vết nào về điều đó.
Cách khắc phục nhanh: Chuyển hướng output trong Crontab
Không cần cài thêm package. Chỉ cần chuyển hướng stdout và stderr trực tiếp trong crontab entry:
# Ghi toàn bộ output vào file log
*/5 * * * * /opt/scripts/backup.sh >> /var/log/backup-cron.log 2>&1
# Bỏ toàn bộ output (chỉ dùng nếu script đã có logging riêng)
*/5 * * * * /opt/scripts/backup.sh > /dev/null 2>&1
Khi cron không còn nơi nào để gửi mail output, cảnh báo MTA sẽ không còn xuất hiện nữa.
Chọn tùy chọn phù hợp với trường hợp của bạn:
- Dùng
>> /var/log/backup-cron.log 2>&1nếu script của bạn không có logging nội bộ. Bạn sẽ cần một bản ghi — đặc biệt khi có sự cố lúc 3 giờ sáng. - Dùng
> /dev/null 2>&1chỉ khi script đã tự ghi log có cấu trúc, hoặc bạn thực sự không cần output đó.
Kiểm tra sau khi sửa
Sau khi cập nhật crontab, chờ lần chạy tiếp theo theo lịch và kiểm tra syslog:
# Theo dõi hoạt động cron theo thời gian thực
tail -f /var/log/syslog | grep CRON
# Trên RHEL/CentOS
tail -f /var/log/cron
Một lần chạy thành công trông như thế này — dòng CMD xuất hiện, không có cảnh báo MTA sau đó:
CRON[12346]: (root) CMD (/opt/scripts/backup.sh)
Sau đó xác nhận file log của bạn đang được ghi:
tail -f /var/log/backup-cron.log
Cách khắc phục triệt để: Đặt MAILTO ở đầu Crontab
Khi bạn quản lý năm cron job? Việc thêm 2>&1 vào từng dòng sẽ nhanh chóng trở nên nhàm chán. Cách gọn hơn: đặt MAILTO thành rỗng một lần ở đầu file. Cron sẽ bỏ qua việc gửi mail cho mọi job trong file đó.
crontab -e
MAILTO=""
*/5 * * * * /opt/scripts/backup.sh >> /var/log/backup-cron.log 2>&1
0 2 * * * /opt/scripts/db-dump.sh >> /var/log/db-dump.log 2>&1
30 6 * * 1 /opt/scripts/weekly-report.sh >> /var/log/weekly.log 2>&1
Vẫn giữ các redirect >> logfile 2>&1 — MAILTO="" chỉ tắt cảnh báo, nhưng bạn vẫn cần output đi đến nơi có ích.
Nếu job thực sự không chạy
Thông báo no MTA installed thực ra là tin vui — nó xác nhận job đã được thực thi. Nhưng nếu không có dòng CMD nào trong syslog, có điều gì đó khác đang sai. Hãy kiểm tra từng bước sau:
1. Xác nhận cron daemon đang chạy
# Debian/Ubuntu
systemctl status cron
# RHEL/CentOS
systemctl status crond
Đang dừng? Khởi động lại: sudo systemctl start cron (hoặc crond).
2. Kiểm tra cú pháp crontab
crontab -l
Ba lỗi thường gặp làm hỏng crontab âm thầm:
- Thiếu dòng mới ở cuối file — entry cuối cùng trong file phải kết thúc bằng một dòng mới, nếu không cron sẽ bỏ qua nó
- Ký tự
%không được thoát — cron hiểu%là ký tự xuống dòng. Phải thoát thành\%trong các lệnh nhưdate +%Y-%m-%d - Sai thứ tự trường — định dạng là
minute hour day month weekday command
Không chắc cron có hoạt động không? Thêm một job thử nghiệm chạy mỗi phút:
* * * * * echo "cron alive $(date)" >> /tmp/cron-test.log 2>&1
Nếu /tmp/cron-test.log có thêm một dòng mới mỗi 60 giây, cron đang hoạt động bình thường và vấn đề nằm ở script của bạn.
3. Kiểm tra quyền thực thi của script
ls -l /opt/scripts/backup.sh
chmod +x /opt/scripts/backup.sh
Script phải có quyền thực thi. Cũng cần kiểm tra rằng người dùng sở hữu crontab có thể đọc và thực thi file — đặc biệt nếu script nằm trong thư mục có quyền truy cập bị hạn chế.
4. PATH trong cron rất hạn chế
PATH mặc định của cron thường chỉ là /usr/bin:/bin. Các lệnh hoạt động tốt trong shell của bạn — python3, aws, node — có thể không tìm thấy được. Có hai cách xử lý:
# Cách A: đặt PATH ở đầu crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * /opt/scripts/backup.sh >> /var/log/backup-cron.log 2>&1
# Cách B: dùng đường dẫn tuyệt đối bên trong script
/usr/bin/python3 /opt/scripts/process.py
5. Biến môi trường không được nạp
Cron không đọc .bashrc hay .bash_profile. Các biến như AWS_PROFILE, JAVA_HOME, hay DATABASE_URL sẽ không tồn tại trừ khi bạn khai báo chúng một cách tường minh. Thêm đoạn này vào đầu script của bạn:
#!/bin/bash
source /etc/environment
# hoặc khai báo biến trực tiếp
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export AWS_PROFILE=production
Tóm tắt nhanh
- Thấy cảnh báo, job đã chạy: Thêm
>> /logfile 2>&1vào crontab entry, hoặc đặtMAILTO=""ở đầu file - Không có dòng CMD trong syslog: Kiểm tra trạng thái daemon, cú pháp crontab, quyền file, và PATH
- Chạy tay được nhưng cron không chạy: Hầu như luôn là do PATH hoặc thiếu biến môi trường

