5 Penyebab Cron Job Tidak Berjalan dan Cara Mengeceknya
Cari tahu mengapa cron job gagal: PATH minimal, tanda persen yang tidak di-escape, crontab keliru, output masuk ke email, atau script mengandalkan shell.
Mengapa cron job Anda tidak berjalan
Cron job yang “tidak pernah berjalan” hampir selalu sebenarnya pernah dijalankan. Job tersebut dijalankan dalam environment yang berbeda dari shell Anda, gagal pada detik pertama, lalu pesannya dikirim ke tempat yang tidak Anda periksa. Lima penyebab menjelaskan hampir semua laporan: search path, tanda persen, file crontab yang salah, output yang dikirim melalui email, dan script yang mengharapkan login session.
cron adalah daemon (service latar belakang) yang membaca file crontab dan menjalankan command sesuai jadwal. cron tidak membaca .bashrc, tidak membuka terminal, tidak memulai 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, dan mulai dari pertanyaan yang mendasari semuanya: apakah cron benar-benar menjalankan job tersebut? “cron tidak pernah memulai job” dan “job sudah dimulai lalu berhenti” adalah masalah yang berbeda dan tidak memiliki penyebab yang sama. Jadi, jawab pertanyaan itu terlebih dahulu.
Apakah cron benar-benar berjalan?
Nama unit daemon 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 menamai unit tersebut cron. Fedora, Rocky, dan Alma menamainya crond. Hanya salah satu nama tersebut yang ada pada mesin tertentu. Jadi, salah satu dari dua perintah yang melaporkan unit tidak dikenal merupakan hal yang normal dan bukan kesalahan.
Baca entri yang ditulis oleh sistem Anda sendiri. Jangan mencari baris yang disalin dari panduan, karena redaksinya berbeda antara implementasi cron dan konfigurasi logging. Anda hanya perlu memeriksa dua hal: apakah terdapat entri pada menit yang ditentukan oleh jadwal Anda, dan apakah entri tersebut mencantumkan perintah Anda. Entri yang mencantumkan perintah Anda berarti cron telah menjalankan tugasnya, sehingga kegagalan terjadi di dalam perintah tersebut. Jika tidak ada entri sama sekali, cron tidak pernah memuat jadwal Anda. Ini adalah penyebab 3 di bawah.
Beberapa image mengirim 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 dan log sama-sama tidak ada, cron mungkin memang belum diinstal. Image cloud minimal dan container sering 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 dipanggil oleh file-file tersebut. Tidak satu pun proses itu berjalan untuk cron job. cron memulai perintah dengan environment singkatnya 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, virtual environment Python, atau workspace Go dapat menjadi penyebabnya. Job gagal pada baris pertamanya, lalu shell menulis error bergaya "not found". Teks persisnya bergantung pada shell yang menjalankannya.
Cari path sebenarnya dari setiap perintah yang digunakan job Anda.
command -v docker
command -v node
readlink -f "$(command -v node)"Selanjutnya, tulis path absolut tersebut ke dalam job, atau tetapkan PATH sekali 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. Ada satu aturan penting: cron tidak melakukan ekspansi variabel pada baris assignment ini. PATH=$PATH:/usr/local/bin menyimpan teks literal $PATH:/usr/local/bin, sehingga job berakhir dengan search path yang tidak berisi direktori yang dapat digunakan. Tulis seluruh daftar tersebut secara eksplisit.
Version manager memerlukan lebih dari sekadar path. nvm, pyenv, rbenv, dan asdf memasang shell function atau direktori shims dari .bashrc Anda, sedangkan cron job tidak pernah membaca file tersebut. Panggil binary berversi menggunakan path absolut, atau source init script milik manager tersebut sebagai baris pertama pada script Anda.
Penyebab 2: tanda persen mengakhiri perintah
Pada kolom perintah crontab, % bukan karakter biasa. % pertama yang tidak di-escape mengakhiri perintah. Semua teks setelahnya diberikan kepada perintah sebagai input standar, dan setiap % berikutnya menjadi baris baru. Ini adalah fitur cron yang memang digunakan untuk memberikan input singkat kepada program. Fitur ini juga menjadi alasan 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 Anda masuk 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 menangani setiap karakter adalah inti solusinya.
Kebiasaan yang lebih aman adalah menyimpan logika sepenuhnya di luar crontab. Masukkan logika tersebut ke dalam script. Di sana, tanda persen tidak memiliki arti khusus.
#!/bin/bash
set -euo pipefail
stamp="$(date +%F)"
tar -czf "/srv/backups/site-${stamp}.tar.gz" /srv/siteBaris crontab kemudian hanya berisi path dan redirect. Crontab yang dapat dibaca sekilas juga 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 debugging pada server yang sama sering kali membaca dua file yang berbeda.sudo crontab -l -u deploymenampilkan crontab milik user lain. Gunakan perintah ini untuk memastikan apa yang benar-benar terpasang bagi 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 berisi huruf, digit, underscore, dan hyphen. File bernamabackup.shatausite.confakan dilewati hanya karena namanya. Ganti namanya menjadibackup, lalu periksa log kembali. - File di
/etc/cron.dharus dimiliki oleh root dan tidak boleh dapat ditulis oleh group atau user lain.ls -l /etc/cron.dmenampilkan kedua fakta tersebut sekaligus. - Script yang ditempatkan di
/etc/cron.dailydan direktori lain yang sejenis 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 memasang crontab.
Pasang crontab user dengan command crontab, bukan dengan mengedit spool file secara manual, karena crontab akan mem-parsing file tersebut 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 milik root membuat file milik root yang mungkin tidak dapat ditulis oleh aplikasi yang membacanya. Job dalam crontab user biasa tidak dapat membaca direktori yang hanya dapat diakses oleh root. Sesuaikan pemilik dengan pekerjaan yang dijalankan: pemeliharaan aplikasi sebaiknya menggunakan account aplikasi itu sendiri. Inilah alasan di balik mengganti WordPress wp-cron dengan system cron job. Mode file yang dibuat oleh job berasal dari umask yang diwarisinya. Nilai tersebut berbeda dari umask shell Anda. Karena itu, baca cara umask menetapkan permission file jika output job tidak dapat dibaca.
Penyebab 4: output dikirim ke email yang tidak dibaca
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, dengan alamat tujuan berupa pemilik crontab atau apa pun yang ditentukan oleh MAILTO. Pada VPS minimal, biasanya tidak ada MTA (mail transfer agent) yang terpasang, sehingga tidak ada yang mengirimkannya. Error Anda ada sesaat, lalu tidak sampai ke mana pun. Itulah satu-satunya alasan job yang bermasalah terlihat tidak menghasilkan output.
Kirim output ke file yang 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 ditempatkan setelah redirect tersebut. Jika urutannya dibalik, seperti pada 2>&1 >> file, standard error tetap menggunakan tujuan awalnya, dan error yang sedang Anda cari justru tidak pernah masuk ke file.
Journal adalah tujuan lain yang tepat. logger menulis ke syslog menggunakan tag yang Anda pilih.
0 3 * * * /usr/local/sbin/backup-site.sh 2>&1 | logger -t backup-siteBaca kembali isinya dengan journalctl -t backup-site. Dengan cara ini, output job berada di dekat entri cron, sehingga urutan waktunya mudah ditelusuri. Jika Anda juga memerlukan catatan tentang siapa yang menjalankan setiap perintah di server, itu merupakan sistem terpisah. Lihat mengaudit perintah pengguna di server Anda untuk penjelasannya.
MAILTO="" di bagian atas crontab menonaktifkan email untuk job di bawahnya. Mengatur MAILTO ke alamat yang valid hanya membantu jika MTA yang berfungsi tersedia. Jadi, pastikan email benar-benar dapat keluar dari server sebelum mengandalkannya.
Satu aturan saat melakukan debugging: jangan pernah menambahkan > /dev/null 2>&1. Baris ini merupakan baris yang paling sering digunakan dalam setiap crontab, tetapi membuang satu-satunya bukti yang Anda miliki. Tambahkan kembali nanti jika diperlukan, setelah job berfungsi.
Penyebab 5: skrip mengandalkan environment yang tidak diberikan oleh cron
Setelah perintah ditemukan dan output-nya ditangkap, yang tersisa adalah semua hal lain yang diberikan session Anda secara otomatis.
- Shell yang digunakan mungkin bukan bash. Periksa dengan
ls -l /bin/sh. Pada Debian dan Ubuntu, perintah tersebut mengarah ke dash, sehingga pengujian dengan kurung ganda, array, dansourcegagal dengan syntax error. Tambahkan baris#!/bin/bashke skrip lalu panggil skrip tersebut, atau tetapkanSHELLdi bagian atas crontab. - Working directory bukan direktori tempat Anda berada sebelumnya. Gunakan path absolut di semua tempat, atau gunakan
cdke direktori tersebut pada baris pertama skrip. Relative path adalah alasan tunggal yang paling umum mengapa sebuah job "berfungsi saat dijalankan secara manual". - Locale yang digunakan bukan locale session Anda. Apa pun yang memformat tanggal atau angka, atau mengurutkan teks, dapat menghasilkan output yang berbeda dengan
LANGyang berbeda. Jika langkah berikutnya mem-parsing output tersebut, tetapkan locale di dalam skrip dan jangan mengandalkan kondisi yang kebetulan sesuai. - Tidak ada TTY (terminal). Perintah yang meminta konfirmasi, membuka editor, atau menampilkan progress bar dapat berhenti atau keluar. Tambahkan flag non-interaktif yang disediakan oleh tool tersebut.
- Tidak ada SSH agent.
SSH_AUTH_SOCKtidak terdapat dalam environment cron, sehingga perintahsshataursyncyang sebelumnya berhasil karena agent Anda sudah memuat key kini gagal melakukan autentikasi. Berikan key khusus untuk job tersebut dan pastikan key dimiliki oleh user yang menjalankan job. - Tidak ada user session bus, sehingga
systemctl --userdari cron job gagal sampaiXDG_RUNTIME_DIRditetapkan. System unit adalah solusi yang lebih baik.
Pada Fedora, Rocky, dan Alma, ada satu tersangka tambahan. SELinux membatasi cron job, sehingga job yang mengakses path dengan label yang 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 yang menunjukkan lingkungan cron
Jangan menebak isi lingkungan cron. Baca isinya secara langsung. Tulis skrip yang mencadangkan semua variabel, 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 pada 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 isinya dengan perintah yang sama saat dijalankan dari shell Anda sendiri. Baris PATH, direktori kerja, dan locale biasanya sudah cukup untuk menjelaskan penyebab kegagalan. Perhatikan dua detail dalam konfigurasi ini: tanda persen berada di dalam skrip sehingga aturan cron tidak berlaku, dan path log dapat ditulis oleh user yang menjalankan job.
Hapus baris crontab tersebut segera setelah Anda menemukan jawabannya. Job yang berjalan setiap menit dan menambahkan data ke file dapat memenuhi disk kecil, dan proses ini berlangsung tanpa suara.
Jadwalnya sudah sesuai dengan yang Anda maksud?
Baris crontab pengguna diawali oleh 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 menjalankan job pada tengah malam setiap tanggal 13, serta pada tengah malam setiap hari Jumat. Untuk mendapatkan satu hari tertentu, biarkan salah satu dari kedua field tersebut tetap *, lalu uji field lainnya di dalam script.
cron menggunakan timezone sistem. Banyak image VPS dikonfigurasi ke UTC (coordinated universal time), sehingga job yang Anda jadwalkan pada 03:00 berjalan pada 03:00 UTC, yang mungkin bertepatan dengan tengah hari di lokasi Anda. timedatectl menampilkan timezone yang benar-benar digunakan oleh server Anda. Periksa nilainya, jangan menganggapnya sama dengan timezone pada laptop Anda.
Ada dua jebakan jadwal lain yang perlu diketahui. @reboot berjalan ketika cron sendiri start. Waktunya tidak sama dengan saat jaringan siap, sehingga 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 start kembali ketika salinan 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.
Alat yang lebih tepat ketika systemd timer digunakan
cron baik untuk satu hal: menjalankan perintah ini pada waktu ini. Di luar itu, kemampuannya terbatas. Timer menyediakan journal tanpa redirect, status keluar yang dapat Anda periksa nanti, pengurutan terhadap network-online.target, serta penundaan acak agar seratus server tidak semuanya mulai pada detik yang sama. Jika pekerjaan Anda memerlukan salah satu fitur tersebut, service dan timer systemd pada VPS lebih mudah daripada mempertahankan satu baris crontab. Saat menulis bagian service, Anda perlu menjawab satu pertanyaan yang tidak pernah diajukan oleh cron, yaitu bagaimana unit mengetahui bahwa pekerjaan benar-benar sudah dimulai. Karena itu, baca terlebih dahulu arti Type= untuk unit simple, forking, dan notify, karena script yang melakukan daemonize sendiri dengan tipe default membuat unit terlihat aktif, padahal tidak ada proses yang berjalan di belakangnya. Perilaku retry juga ditentukan di sana, karena kebijakan restart systemd menentukan tindakan setelah terjadi kegagalan, sedangkan cron sama sekali tidak memiliki jawaban untuk pertanyaan tersebut.
Gunakan cron untuk pekerjaan kecil. Pindahkan pekerjaan yang memiliki dependensi atau kebijakan retry ke timer. Keduanya dapat berjalan pada server yang sama, jadi Anda tidak perlu menyelesaikan migrasi ini dalam satu sesi.
FAQ
Mengapa cron job saya berjalan jika dijalankan manual, tetapi gagal saat dijalankan oleh cron?
Karena environment shell Anda berbeda dari environment cron. 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 absolute path untuk setiap perintah, tetapkan semua yang diperlukan di bagian atas crontab atau di dalam script, lalu jadwalkan probe job satu menit yang menjalankan env | sort, pwd, dan id ke dalam file log. Dengan demikian, Anda dapat membaca environment cron yang sebenarnya tanpa menebak-nebak.
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 tersebut justru diteruskan melalui rsyslog ke file di bawah /var/log. Cari entri pada menit yang ditentukan oleh jadwal Anda dan 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 sudah dimulai lalu berhenti. Tangkap output-nya dengan redirect.
Mengapa date +%Y bermasalah di dalam crontab?
cron memperlakukan % secara khusus pada command field. % pertama yang tidak di-escape mengakhiri perintah. Semua teks setelahnya diteruskan ke perintah tersebut sebagai standard input, dan setiap % berikutnya menjadi newline. Karena itu, filename dengan format tanggal tidak pernah diterima oleh program yang Anda tuju. Escape setiap tanda persen sebagai \%, atau pindahkan perintah ke dalam 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 local mail system, kepada pemilik crontab atau kepada alamat yang ditentukan oleh MAILTO. Sebagian besar image VPS tidak memasang mail transfer agent, sehingga pesan tersebut dibuang dan job terlihat tidak menghasilkan output. Redirect output ke file menggunakan >> /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 Anda masih melakukan debugging.
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 dikueri, pengurutan setelah network aktif, randomized start delay, atau kebijakan retry setelah kegagalan. Keduanya dapat berjalan pada server yang sama. Anda dapat memindahkan job satu per satu sesuai kebutuhan.