Penjadwalan tugas

Definisi jadwal di routes/console.php dan entri cron yang menjalankannya.

Daftar isi

Laravel menjadwalkan tugas berulang melalui satu mekanisme terpusat, bukan melalui banyak entri cron yang terpisah. Topik ini membahas peran perintah schedule:run beserta entri cron yang memanggilnya setiap menit, cara mendefinisikan jadwal di routes/console.php, pemetaan metode frekuensi ke ekspresi cron, serta cara memeriksa dan menjalankan jadwal pada proyek aplikasi catatan.

Peran scheduler dan entri cron#

Aplikasi web hanya aktif ketika ada permintaan, sehingga tugas berulang seperti ekspor laporan harian tidak dapat mengandalkan siklus permintaan. Laravel menyelesaikannya dengan scheduler: satu entri cron memanggil php artisan schedule:run setiap menit, kemudian perintah itu membaca definisi jadwal aplikasi dan menjalankan yang jatuh tempo. Definisi jadwal tersimpan di dalam kode bersama sisa aplikasinya, sedangkan server hanya memuat satu entri cron untuk seluruh tugas. Diagram berikut merangkum alurnya.

crontab server* * * * *setiap menitphp artisanschedule:rundipanggil cronPeriksa definisiroutes/console.phppilih yang jatuh tempoTugasberjalanSatu entri cron memanggil schedule:run setiap menit, dan perintah itu menjalankan tugas yang jatuh tempo.
Alur penjadwalan dari entri cron hingga tugas berjalan
Entri cron ditambahkan sekali pada setiap server produksi melalui perintah crontab -e. Bentuknya seperti berikut, dengan lima bidang waktu di depan dan perintahnya di belakang.

text Entri cron untuk scheduler
* * * * * cd /home/kal/aplikasi-catatan && php artisan schedule:run >> /dev/null 2>&1

Lima bidang * * * * * berarti setiap menit pada setiap jam, hari, bulan, dan hari dalam minggu. Perintah di belakangnya masuk ke direktori proyek terlebih dahulu sehingga artisan menemukan aplikasinya, menjalankan schedule:run, lalu mengarahkan keluarannya ke /dev/null agar kotak surat cron tidak terisi. Bidang-bidang waktu tersebut menggunakan konvensi yang sama dengan ekspresi cron pada definisi jadwal, sehingga keduanya mengikuti format yang seragam.

Pada lingkungan lokal yang belum memiliki daemon cron, perintah php artisan schedule:work menggantikan entri cron dengan memanggil schedule:run setiap menit. Perintah ini berjalan terus sampai dihentikan, sehingga sesuai untuk pengembangan dan bukan untuk produksi.

bash
# tidak-dijalankan
# menjalankan scheduler secara berkelanjutan pada lingkungan lokal
cd ~/aplikasi-catatan
php artisan schedule:work

Mendefinisikan jadwal di routes/console.php#

Berkas routes/console.php dimuat framework pada setiap proses, sehingga pemanggilan facade Schedule di berkas itu mendaftarkan event jadwal ke scheduler. Berkas yang sama juga menjadi tempat definisi perintah Artisan berbasis closure, dan penempatan ini wajar karena jadwal sering menunjuk perintah yang didefinisikan di sana. Proyek yang masih polos belum memiliki jadwal apa pun, dan keadaan itu dapat diperiksa dengan schedule:list.

bash
# memeriksa daftar jadwal pada proyek yang belum mendefinisikannya
cd ~/aplikasi-catatan
php artisan schedule:list
text Keluaran
   INFO  No scheduled tasks have been defined.

Perbarui berkas tersebut sehingga mendaftarkan satu perintah closure beserta jadwalnya. Perintah catatan:ekspor berperan sebagai contoh tugas, sedangkan Schedule::command() dengan metode everyMinute() menjadwalkannya untuk berjalan setiap menit.

php console.php
// tidak-dijalankan
<?php

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;

Artisan::command('catatan:ekspor', function () {
    $this->info('Laporan catatan diekspor ke berkas CSV.');
})->purpose('Mengekspor laporan catatan ke berkas CSV');

Schedule::command('catatan:ekspor')->everyMinute()->description('Ekspor laporan catatan');

Blok pertama mendefinisikan perintah catatan:ekspor yang keluarannya ditulis dengan $this->info(), sedangkan blok kedua menjadwalkannya dan memberi deskripsi melalui description(). Metode command() menerima tanda tangan (signature) perintah, sehingga jadwal juga dapat menunjuk perintah kelas maupun perintah bawaan framework. Apabila tugasnya hanya satu langkah pendek, Schedule::call(function () { /* aksi */ }) menjadwalkan closure tanpa membuat perintah sama sekali. Setelah definisi ini ditambahkan, schedule:list menampilkan jadwalnya beserta ekspresi cron dan perkiraan pemanggilan berikutnya.

text Hasil perintah schedule:list
  * * * * *  php artisan catatan:ekspor ........ Next Due: 20 seconds from now

Metode frekuensi dan ekspresi cron#

Metode frekuensi pada definisi jadwal diterjemahkan menjadi ekspresi cron, yaitu lima bidang yang sama dengan yang digunakan crontab. Tabel berikut memetakan beberapa metode yang sering digunakan beserta artinya.

MetodeEkspresi cronArti
everyMinute()* * * * *setiap menit
dailyAt("06:00")0 6 * * *setiap hari pukul 06.00
weeklyOn(1, "08:00")0 8 * * 1setiap Senin pukul 08.00
monthlyOn(28, "15:00")0 15 28 * *tanggal 28 pukul 15.00

Ekspresi tersebut tersimpan pada properti expression setiap event jadwal, sehingga pemetaannya dapat dibaca langsung dari proyek.

bash
# membaca ekspresi cron dari beberapa metode frekuensi
cd ~/aplikasi-catatan
php artisan tinker --execute='
use Illuminate\Support\Facades\Schedule;

echo Schedule::command("catatan:ekspor")->everyMinute()->expression . PHP_EOL;
echo Schedule::command("catatan:ekspor")->dailyAt("06:00")->expression . PHP_EOL;
echo Schedule::command("catatan:ekspor")->weeklyOn(1, "08:00")->expression . PHP_EOL;
echo Schedule::command("catatan:ekspor")->monthlyOn(28, "15:00")->expression . PHP_EOL;
'
text Keluaran
* * * * *
0 6 * * *
0 8 * * 1
0 15 28 * *

Setiap baris cocok dengan tabel di atas, dan angka pada bidang tertentu menegaskan perilakunya, misalnya 0 8 * * 1 yang bidang terakhirnya berarti hari ke-1 dalam minggu, yaitu Senin. Kombinasi lain juga tersedia, misalnya weekdays() untuk membatasi pada hari kerja dan between("08:00", "17:00") untuk membatasi rentang jam. Untuk tugas yang berpotensi berjalan lebih lama dari jeda jadwalnya, metode withoutOverlapping() menahan pemanggilan berikutnya sampai yang sebelumnya selesai, sedangkan onOneServer() mencegah tugas yang sama dijalankan ganda ketika aplikasi ditempatkan pada beberapa server.

Menjalankan tugas yang jatuh tempo#

Pada server, entri cron memanggil schedule:run tanpa keluaran yang terlihat, sehingga perilakunya perlu dipahami melalui simulasi. Perintah php artisan schedule:run memeriksa seluruh event yang terdaftar, menjalankan yang jatuh tempo pada menit itu, lalu melaporkan pelaksanaannya ke konsol.

bash
# tidak-dijalankan
# menjalankan seluruh tugas yang jatuh tempo pada menit ini
cd ~/aplikasi-catatan
php artisan schedule:run

Keluaran berikut diambil dari eksekusi nyata pada proyek yang berisi jadwal di atas.

text Hasil perintah schedule:run
  2026-11-03 14:26:39 Running ['artisan' catatan:ekspor] ....... 123.07ms DONE
  ⇂ Ekspor laporan catatan

Baris pertama memuat waktu mulai, identitas event, durasi eksekusi, dan statusnya. Baris kedua menampilkan deskripsi yang ditetapkan melalui description(), atau perintah lengkap yang dieksekusi apabila deskripsi tidak diberikan. Keluaran tugas itu sendiri tidak tampak pada keluaran tersebut karena entri cron mengarahkannya ke /dev/null, dan apabila keluaran perlu disimpan, metode sendOutputTo() pada event mengarahkan hasilnya ke berkas tertentu. Pelaksanaan rutin pada produksi tidak dipantau secara langsung, melainkan melalui log dan mekanisme pemberitahuan yang disiapkan aplikasi.

Kesalahan umum#

Kesalahan pertama adalah menambahkan definisi jadwal tanpa memasang entri cron. Tidak ada satu pun tugas yang berjalan karena schedule:run tidak pernah dipanggil, dan schedule:list tetap menampilkan definisinya sehingga keadaannya tampak sehat. Pasang entri cron pada setiap server produksi, dan gunakan schedule:work hanya pada lingkungan lokal.

Kesalahan kedua adalah menggunakan jalur relatif pada entri cron, misalnya php artisan schedule:run tanpa cd. Cron menjalankan perintah dari direktori kerja yang bukan direktori proyek, sehingga artisan tidak menemukan aplikasinya dan cron mencatat error yang sama setiap menit. Selalu masuk ke direktori proyek terlebih dahulu, atau tulis jalur absolut menuju artisan.

Kesalahan ketiga adalah membiarkan tugas berjalan lebih lama dari jeda jadwalnya. Tugas ekspor yang membutuhkan tiga menit pada jadwal everyMinute() bertumpuk menjadi tiga proses yang bersaing menggunakan sumber daya yang sama. Tandai tugas semacam itu dengan withoutOverlapping() agar pemanggilan berikutnya menunggu, lalu pantau durasi eksekusinya pada keluaran schedule:run.

Ringkasan#

  • Satu entri cron * * * * * memanggil schedule:run setiap menit, dan perintah itu menjalankan seluruh tugas yang jatuh tempo.
  • Jadwal didefinisikan di routes/console.php melalui facade Schedule, dengan metode frekuensi yang diterjemahkan menjadi ekspresi cron lima bidang.
  • schedule:list menampilkan daftar jadwal beserta ekspresi cron-nya, sedangkan schedule:work menggantikan cron pada lingkungan lokal.
  • withoutOverlapping() mencegah tugas bertumpuk, dan description() membuat laporan pelaksanaannya mudah dibaca.

Referensi#