Mengapa Cron Job Tidak Berjalan? Ini 5 Penyebabnya
Temukan lima penyebab cron job gagal berjalan: PATH minimal, tanda persen yang tidak di-escape, crontab keliru, output masuk ke mail, dan asumsi shell.
Mengapa cron job tidak berjalan
Cron job yang “tidak pernah berjalan” hampir selalu sebenarnya pernah dijalankan. Job tersebut berjalan dalam environment yang berbeda dari shell Anda, gagal pada detik pertama, lalu pesannya masuk ke tempat yang tidak Anda baca. Lima penyebab menjelaskan hampir semua laporan: search path, tanda persen, file crontab yang salah, output yang dikirim melalui mail, dan script yang mengharapkan login session.
cron adalah daemon (service yang berjalan di latar belakang) yang membaca file crontab lalu menjalankan command sesuai jadwal. cron tidak membaca .bashrc, tidak membuka terminal, tidak menjalankan login shell, dan tidak memberi tahu Anda saat sebuah command gagal. Semua penyebab di bawah ini berasal dari keempat fakta tersebut.
Periksa penyebabnya secara berurutan. Mulailah dari pertanyaan yang mendasari semuanya: apakah cron benar-benar menjalankan job tersebut? “cron tidak pernah memulai job” dan “job sudah dimulai lalu berhenti” adalah dua masalah berbeda yang tidak berkaitan. Jadi, jawab pertanyaan itu terlebih dahulu.
Apakah cron benar-benar berjalan?
Daemon ini menggunakan nama unit yang berbeda pada setiap keluarga distribusi. Periksa keduanya, lalu baca log.
systemctl status cron
systemctl status crond
journalctl -u cron --since "2 hours ago"
journalctl -u crond --since "2 hours ago"Debian dan Ubuntu menggunakan cron untuk unit tersebut. Fedora, Rocky, dan Alma menggunakan crond. Hanya salah satu nama tersebut yang ada pada mesin tertentu. Jadi, salah satu dari dua perintah yang melaporkan unit tidak dikenal adalah hal yang normal, bukan kesalahan.
Baca entri yang ditulis oleh sistem Anda sendiri. Jangan mencari baris yang disalin dari panduan, karena susunan pesannya berbeda antarimplementasi cron dan konfigurasi logging. Anda hanya perlu memeriksa dua hal: apakah ada entri pada menit yang ditentukan oleh jadwal Anda, dan apakah entri tersebut mencantumkan perintah Anda. Entri yang mencantumkan perintah Anda berarti cron telah menjalankan bagiannya, sehingga kegagalan terjadi di dalam perintah tersebut. Tidak adanya entri berarti cron tidak pernah memiliki jadwal Anda. Ini adalah penyebab 3 di bawah.
Beberapa image mengirimkan pesan cron melalui rsyslog ke sebuah file, bukan ke journal. Periksa /var/log untuk mencari file dengan nama yang merujuk pada cron atau syslog, lalu baca bagian akhirnya.
ls -l /var/log
sudo tail -n 50 /var/log/syslogJika unit maupun log tidak ada, cron mungkin memang belum terpasang. Image cloud minimal dan container sering kali tidak menyertakannya.
dpkg -l cron
rpm -q cronie
sudo apt install cron
sudo dnf install cronie
sudo systemctl enable --now cronPenyebab 1: cron tidak memiliki PATH Anda
Shell interaktif Anda membentuk PATH dari /etc/profile, ~/.profile, ~/.bashrc, dan semua file yang di-source oleh file-file tersebut. Tidak satu pun proses itu berjalan untuk cron job. cron memulai perintah dengan environment singkat miliknya sendiri, sehingga program yang berada di luar direktori sistem standar tidak ditemukan. Apa pun yang berada di bawah /usr/local/bin, /opt, version manager bahasa pemrograman, Python virtual environment, atau Go workspace dapat menjadi penyebabnya. Job gagal pada baris pertamanya, lalu shell menulis error bergaya "not found". Kata-kata persisnya bergantung pada shell yang menjalankannya.
Temukan path sebenarnya untuk setiap perintah yang digunakan job Anda.
command -v docker
command -v node
readlink -f "$(command -v node)"Kemudian, tulis path absolut tersebut di dalam job, atau tetapkan PATH satu kali di bagian atas crontab.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /usr/local/bin/mytool runAmbil daftar tersebut dari mesin Anda sendiri dengan echo "$PATH", lalu hapus apa pun yang hanya tersedia di dalam sesi interaktif. Satu aturan penting berlaku di sini: cron tidak memperluas variabel dalam baris assignment ini. PATH=$PATH:/usr/local/bin menyimpan teks literal $PATH:/usr/local/bin, sehingga job akhirnya memiliki search path yang tidak berisi direktori yang dapat digunakan. Tulis seluruh daftarnya secara lengkap.
Version manager memerlukan lebih dari sekadar path. nvm, pyenv, rbenv, dan asdf memasang shell function atau direktori shims dari .bashrc, sedangkan cron job tidak pernah membaca file tersebut. Panggil binary berversi menggunakan path absolut, atau source init script milik manager tersebut sebagai baris pertama script Anda.
Penyebab 2: tanda persen mengakhiri perintah Anda
Pada kolom perintah crontab, % bukan karakter biasa. % pertama yang tidak di-escape mengakhiri perintah. Semua teks setelahnya diteruskan ke perintah sebagai input standar, dan setiap % berikutnya diubah menjadi baris baru. Ini adalah fitur cron yang memang digunakan untuk memberikan input singkat kepada program. Fitur ini juga menjadi alasan mengapa nama file dengan tanggal merupakan contoh klasik entri crontab yang rusak.
Tulis 0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +%F).tar.gz /srv/site, dan tar tidak pernah menerima tanggal yang telah diformat. cron memotong baris pada % pertama. Akibatnya, shell menerima substitusi perintah yang belum selesai, sedangkan sisa baris diterima sebagai input standar. Escape setiap tanda persen dengan garis miring terbalik.
0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +\%F).tar.gz /srv/siteDua lapisan membaca satu baris tersebut secara berurutan. \% adalah aturan cron yang diterapkan oleh cron sebelum menjalankan apa pun. $(date +\%F) adalah substitusi perintah yang diterapkan kemudian oleh shell yang dijalankan cron. Memahami lapisan yang mengelola setiap karakter adalah kunci utamanya.
Kebiasaan yang lebih aman adalah menyimpan logika sepenuhnya di luar crontab. Masukkan logika tersebut ke dalam skrip, karena tanda persen tidak memiliki makna khusus di sana.
#!/bin/bash
set -euo pipefail
stamp="$(date +%F)"
tar -czf "/srv/backups/site-${stamp}.tar.gz" /srv/siteBaris crontab kemudian hanya berisi path dan pengalihan output. Crontab yang dapat dibaca sekilas akan lebih mudah di-debug.
Penyebab 3: crontab mana yang Anda edit?
Tidak ada satu crontab saja. Ada beberapa file dengan pemilik dan jumlah field yang berbeda. Job yang ditulis ke file yang salah tidak akan terlihat.
crontab -emengedit crontab milik user yang menjalankan perintah tersebut.sudo crontab -emengedit crontab milik root. Dua orang yang melakukan debug pada server yang sama sering kali membaca dua file yang berbeda.sudo crontab -l -u deploymenampilkan crontab user lain. Gunakan perintah ini untuk memastikan apa yang benar-benar terpasang pada account yang seharusnya menjalankan job./etc/crontabdan setiap file di/etc/cron.dmemiliki satu field tambahan di antara jadwal dan command: user yang akan digunakan untuk menjalankan job. Jika Anda menempelkan baris crontab user dengan lima field ke/etc/cron.d, kata pertama pada command akan dibaca sebagai username.- File di
/etc/cron.dharus diberi nama yang hanya menggunakan huruf, digit, underscore, dan hyphen. File bernamabackup.shatausite.confakan dilewati hanya karena namanya. Ubah namanya menjadibackup, lalu periksa log lagi. - File di
/etc/cron.dharus dimiliki oleh root dan tidak boleh dapat ditulis oleh group atau user lain.ls -l /etc/cron.dmenampilkan kedua informasi tersebut sekaligus. - Script yang ditempatkan di
/etc/cron.dailydan direktori sejenisnya mengikuti aturan penamaan yang sama dan juga harus memiliki execute bit. Execute bit yang tidak ada menyebabkan file dilewati tanpa pesan. /etc/cron.allowdan/etc/cron.denymenentukan siapa yang boleh memasang crontab. Jika salah satunya ada di server Anda, baca file tersebut sebelum menganggap user Anda diizinkan memiliki crontab.
Pasang crontab user dengan command crontab, bukan dengan mengedit spool file secara manual, karena crontab mem-parsing file sebelum memasangnya. Setelah menyimpan, baca output yang ditampilkan oleh command tersebut. Jika file ditolak, versi sebelumnya tetap aktif dan perubahan Anda tidak pernah diterapkan. Kondisi ini terlihat persis seperti cron mengabaikan Anda.
Pemilik file juga menentukan permission. Job dalam crontab root membuat file yang dimiliki root, sehingga aplikasi yang membacanya mungkin tidak dapat menulis ke file tersebut. Job dalam crontab user biasa tidak dapat membaca direktori yang hanya dapat diakses oleh root. Sesuaikan pemilik dengan pekerjaan yang dilakukan: pemeliharaan aplikasi seharusnya dijalankan oleh account milik aplikasi tersebut. Inilah alasan di balik mengganti wp-cron WordPress dengan system cron job. Mode file yang dibuat oleh job berasal dari umask yang diwarisinya. Nilai ini berbeda dari umask shell Anda. Karena itu, baca cara umask mengatur permission file jika output job tidak dapat dibaca.
Penyebab 4: output dikirim ke email yang tidak dibaca siapa pun
cron mengumpulkan semua output yang ditulis job ke standard output dan standard error. Jika job menulis sesuatu, cron meneruskan teks tersebut ke sistem email lokal. Alamat tujuannya adalah pemilik crontab atau alamat yang ditentukan oleh MAILTO. Pada VPS minimal, biasanya tidak ada MTA (mail transfer agent) yang terpasang, sehingga tidak ada yang mengirimkannya. Error Anda hanya ada sesaat, lalu hilang tanpa tujuan. Itulah sebabnya job yang rusak terlihat tidak menghasilkan apa pun.
Alihkan output ke file yang dapat Anda kelola.
0 3 * * * /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1>> menambahkan standard output ke file. 2>&1 mengarahkan standard error ke tujuan standard output saat itu, sehingga harus ditulis setelah pengalihan tersebut. Jika urutannya dibalik menjadi 2>&1 >> file, standard error tetap menggunakan tujuan awalnya. Error yang sedang Anda cari justru tidak pernah masuk ke file.
Journal adalah tujuan lain yang baik. logger menulis output ke syslog menggunakan tag yang Anda pilih.
0 3 * * * /usr/local/sbin/backup-site.sh 2>&1 | logger -t backup-siteBaca kembali output tersebut dengan journalctl -t backup-site. Dengan cara ini, output job tersimpan di dekat entri cron, sehingga urutan waktunya mudah diikuti. Jika Anda juga memerlukan catatan tentang siapa yang menjalankan setiap perintah pada server, gunakan sistem terpisah. audit perintah pengguna pada server Anda membahasnya.
MAILTO="" di bagian atas crontab menonaktifkan email untuk job di bawahnya. Mengatur MAILTO ke alamat yang valid hanya membantu jika MTA yang berfungsi tersedia. Karena itu, pastikan email benar-benar dapat keluar dari server sebelum mengandalkannya.
Satu aturan saat melakukan debugging: jangan pernah menambahkan > /dev/null 2>&1. Baris ini sangat umum terdapat di setiap crontab, tetapi membuang satu-satunya bukti yang Anda miliki. Tambahkan kembali nanti setelah job berhasil berjalan, jika memang diperlukan.
Penyebab 5: skrip mengasumsikan environment yang tidak diberikan oleh cron
Setelah command ditemukan dan output-nya ditangkap, yang tersisa adalah semua hal lain yang biasanya diberikan oleh session Anda secara otomatis.
- Shell yang digunakan mungkin bukan bash. Periksa dengan
ls -l /bin/sh. Pada Debian dan Ubuntu, command tersebut mengarah ke dash. Akibatnya, pengujian double bracket, array, dansourcegagal dengan syntax error. Berikan skrip baris#!/bin/bashlalu jalankan skrip tersebut, atau tetapkanSHELLdi bagian atas crontab. - Working directory bukan direktori tempat Anda berada sebelumnya. Gunakan absolute path di semua tempat, atau gunakan
cduntuk berpindah ke direktori tersebut pada baris pertama skrip. Relative path adalah alasan tunggal yang paling umum mengapa sebuah job "berjalan saat saya menjalankannya secara manual". - Locale bukan locale session Anda. Apa pun yang memformat tanggal atau angka, atau mengurutkan teks, dapat menghasilkan output yang berbeda di bawah
LANGyang berbeda. Jika langkah berikutnya memproses output tersebut, tetapkan locale di dalam skrip dan jangan mengandalkan environment. - Tidak ada TTY (terminal). Command yang meminta konfirmasi, membuka editor, atau menampilkan progress bar dapat macet atau keluar. Tambahkan flag non-interaktif yang tersedia pada tool tersebut.
- Tidak ada SSH agent.
SSH_AUTH_SOCKtidak tersedia di environment cron. Karena itu, commandsshataursyncyang sebelumnya berhasil karena agent Anda telah dimuat kini gagal melakukan autentikasi. Berikan job tersebut key sendiri yang dimiliki oleh user yang menjalankan job. - Tidak ada user session bus. Karena itu,
systemctl --userdari cron job gagal sampaiXDG_RUNTIME_DIRditetapkan. System unit adalah solusi yang lebih baik.
Pada Fedora, Rocky, dan Alma, ada satu kemungkinan tambahan. SELinux membatasi cron job. Karena itu, job yang mengakses path dengan label tidak sesuai akan ditolak meskipun permission file terlihat benar. Periksa penolakan dengan sudo ausearch -m avc -ts recent, lalu baca Dasar-dasar SELinux untuk server sebelum menonaktifkan apa pun.
Probe satu menit untuk menampilkan lingkungan cron
Jangan menebak isi lingkungan cron. Baca langsung. Tulis skrip yang membuang semua variabel lingkungan, jadwalkan skrip tersebut setiap menit, tunggu, lalu baca filenya.
cat > /home/deploy/cron-probe.sh <<'EOF'
#!/bin/bash
echo "=== probe ==="
date -Is
pwd
id
echo "SHELL=$SHELL"
echo "LANG=$LANG"
command -v node || echo "node is not on this PATH"
env | sort
EOF
chmod +x /home/deploy/cron-probe.shTambahkan satu baris ke crontab milik user yang menjalankan job sebenarnya. Gunakan path absolut di kedua sisi.
* * * * * /home/deploy/cron-probe.sh >> /home/deploy/cron-probe.log 2>&1Tunggu satu menit, lalu baca /home/deploy/cron-probe.log dan bandingkan dengan perintah yang sama saat dijalankan dari shell Anda sendiri. Baris PATH, direktori kerja, dan locale biasanya sudah cukup untuk menjelaskan kegagalannya. Perhatikan dua detail dalam konfigurasi ini: tanda persen berada di dalam skrip sehingga aturan cron tidak berlaku, dan path log dapat ditulisi oleh user yang menjalankan job.
Hapus baris crontab tersebut segera setelah Anda menemukan jawabannya. Job yang berjalan setiap menit dan menambahkan data ke file akan memenuhi disk kecil, dan proses itu berlangsung tanpa menampilkan pesan.
Apakah jadwalnya sudah sesuai yang Anda maksud?
Baris crontab pengguna diawali dengan lima field: menit, jam, hari dalam bulan, bulan, dan hari dalam minggu. Dua field di antaranya berinteraksi dengan cara yang sering mengejutkan pengguna.
Jika hari dalam bulan dan hari dalam minggu sama-sama dibatasi, artinya keduanya bukan *, cron menjalankan job ketika salah satu field cocok. 0 0 13 * 5 bukan berarti "Jumat tanggal 13". Baris tersebut berjalan pada tengah malam setiap tanggal 13, dan pada tengah malam setiap hari Jumat. Untuk memilih satu hari tertentu, biarkan salah satu dari dua field tersebut tetap *, lalu lakukan pengujian terhadap field lainnya di dalam script.
cron menggunakan timezone sistem. Banyak image VPS dikonfigurasi menggunakan UTC (coordinated universal time), sehingga job yang Anda jadwalkan pada 03:00 berjalan pada 03:00 UTC. Waktu tersebut mungkin berada di tengah siang atau sore Anda. timedatectl menampilkan timezone yang sebenarnya digunakan oleh server Anda. Periksa hasilnya, dan jangan berasumsi bahwa timezone tersebut sama dengan timezone laptop Anda.
Ada dua kesalahan umum lain pada jadwal yang perlu diketahui. @reboot dijalankan ketika cron sendiri mulai, bukan ketika jaringan sudah siap. Karena itu, job yang memerlukan DNS atau host remote dapat gagal saat boot, lalu berhasil pada setiap eksekusi manual berikutnya. Selain itu, tidak ada yang mencegah job yang lambat untuk dimulai lagi ketika proses sebelumnya masih berjalan. Bungkus job tersebut dengan lock.
*/5 * * * * /usr/bin/flock -n /tmp/backup-site.lock /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1flock -n langsung berhenti jika lock sudah digunakan, sehingga eksekusi yang tumpang tindih berhenti dan tidak berjalan bersamaan dengan eksekusi pertama.
Kapan systemd timer menjadi pilihan yang lebih tepat
cron unggul untuk satu hal: menjalankan perintah ini pada waktu tersebut. Untuk kebutuhan lain, cron memiliki banyak keterbatasan. Timer menyediakan journal tanpa redirect, status keluar yang dapat Anda periksa kemudian, pengurutan terhadap network-online.target, serta penundaan acak agar seratus server tidak semuanya mulai pada detik yang sama. Jika tugas Anda memerlukan salah satu fitur tersebut, service dan timer systemd pada VPS membutuhkan lebih sedikit pekerjaan daripada mempertahankan satu baris crontab. Perilaku percobaan ulang juga sebaiknya ditangani di sana, karena kebijakan restart systemd menentukan tindakan setelah kegagalan, sedangkan cron sama sekali tidak memiliki jawaban untuk hal tersebut.
Gunakan cron untuk tugas-tugas kecil. Pindahkan tugas yang memiliki dependensi atau kebijakan percobaan ulang ke timer. Keduanya dapat berjalan pada server yang sama, jadi migrasi ini tidak harus diselesaikan dalam satu waktu.
FAQ
Mengapa cron job saya berjalan jika dijalankan manual, tetapi gagal saat dijalankan oleh cron?
Karena lingkungan shell dan cron berbeda. Login shell membaca /etc/profile dan ~/.bashrc, yang menetapkan PATH, locale, serta variabel agent Anda. cron menjalankan perintah tanpa semua itu, dari working directory yang berbeda, dan terkadang menggunakan shell yang berbeda. Gunakan path absolut untuk setiap perintah. Tetapkan hal yang diperlukan di bagian atas crontab atau di dalam script. Jadwalkan probe job dengan jeda satu menit yang menjalankan env | sort, pwd, dan id ke dalam file log. Dengan demikian, Anda dapat membaca lingkungan cron yang sebenarnya tanpa menebaknya.
Bagaimana cara memeriksa apakah cron benar-benar menjalankan job saya?
Baca log daemon. Gunakan journalctl -u cron pada Debian dan Ubuntu, atau journalctl -u crond pada Fedora, Rocky, dan Alma. Pada sebagian image, pesan dialihkan melalui rsyslog ke file di bawah /var/log. Cari entri pada menit yang ditentukan oleh jadwal Anda. Pastikan entri tersebut mencantumkan perintah Anda. Tidak adanya entri berarti cron tidak pernah menerima jadwal tersebut. Pastikan Anda mengedit crontab yang benar. Entri tanpa hasil berarti perintah telah dimulai lalu berhenti. Tangkap output-nya dengan redirect.
Mengapa date +%Y gagal digunakan di dalam crontab?
cron memperlakukan % secara khusus di field perintah. % pertama yang tidak di-escape mengakhiri perintah. Semua teks setelahnya diteruskan ke perintah tersebut sebagai standard input. Setiap % berikutnya menjadi newline. Karena itu, nama file dengan format tanggal tidak pernah sampai ke program yang seharusnya menerimanya. Escape setiap tanda persen sebagai \%. Alternatifnya, pindahkan perintah ke script lalu panggil script tersebut dari cron. Di dalam script, tanda persen tidak memiliki arti khusus.
Ke mana output cron job saya dikirim?
Output dikirim ke sistem mail lokal, kepada pemilik crontab atau alamat yang ditentukan oleh MAILTO. Sebagian besar image VPS tidak memasang mail transfer agent. Akibatnya, pesan dibuang dan job tampak tidak menghasilkan output. Redirect output ke file dengan >> /path/to/log 2>&1. Pertahankan urutan tersebut agar standard error mengikuti standard output. Alternatifnya, pipe output melalui logger -t myjob lalu baca kembali dengan journalctl -t myjob. Jangan gunakan > /dev/null 2>&1 selama proses debugging masih berlangsung.
Apakah sebaiknya saya menggunakan cron atau systemd timer?
Gunakan cron untuk perintah sederhana pada waktu tetap, terutama jika job tersebut mungkin perlu dipindahkan ke mesin yang tidak menjalankan systemd. Gunakan timer jika Anda ingin output tersimpan di journal tanpa redirect, exit status yang dapat ditanyakan, pengurutan setelah network aktif, penundaan start acak, atau kebijakan retry setelah kegagalan. Keduanya dapat berjalan pada server yang sama. Anda dapat memindahkan job satu per satu saat diperlukan.