Setiap aplikasi dewasa punya pekerjaan yang harus berulang: kirim laporan tiap pagi, bersihkan tabel yang membengkak, sinkronisasi data ke layanan eksternal. Solusi klasik adalah menulis baris di crontab server — dan di situlah masalahnya: begitu aplikasi tumbuh, baris cron menyebar ke banyak server, tidak ikut ter-deploy, dan tidak pernah terekam di Git. Laravel menawarkan pembalikan pendekatan lewat task scheduler: seluruh jadwal didefinisikan sebagai kode PHP di satu berkas, sementara crontab hanya perlu satu baris. Pada chapter ini kita bedah mengapa pendekatan terpusat ini menyelesaikan masalah nyata operasional, sebelum menulis jadwal pertama kita di chapter berikutnya.
Masalah Cron yang Tersebar #
Cron bekerja dengan prinsip sederhana: sistem operasi mengeksekusi perintah pada menit-menit yang cocok dengan pola lima field (menit jam tanggal bulan hari). Masalahnya bukan pada mekanismenya, melainkan pada cara kita mengelolanya:
- Per-server: crontab hidup di tiap server. Tiga application server berarti tiga tempat berbeda yang harus dikonfigurasi dan disinkronkan manual.
- Tak terlihat: tidak ada perintah “tampilkan semua jadwal aplikasi” — kita harus SSH ke setiap server dan membaca
crontab -l. - Tanpa riwayat: crontab tidak masuk Git, sehingga perubahan jadwal tidak ikut di-review, tidak bisa di-rollback, dan hanya diketahui admin yang menuliskannya.
Skenario khasnya: developer menambahkan emails:send di crontab server staging, lupa menyalinnya ke production, dan laporan harian berhenti terkirim tanpa error di mana pun. Scheduler Laravel dirancang untuk menutup celah operasional seperti ini.
Scheduler Terpusat: Satu Entri Cron #
Laravel membalik arsitekturnya. Kita menulis seluruh jadwal di dalam aplikasi, lalu crontab hanya memanggil satu perintah ini setiap menit:
* * * * * cd /var/www/aplikasi && php artisan schedule:run >> /dev/null 2>&1Perintah schedule:run memeriksa jadwal yang kita definisikan, lalu mengeksekusi task yang jatuh temponya pada menit ini. Ketika tidak ada yang jatuh tempo, perintah ini keluar dengan tenang:
php artisan schedule:runOutput yang Dihasilkan:
INFO No scheduled commands are ready to run. Alur lengkapnya tiap menit terlihat seperti ini:
Karena hanya ada satu titik masuk, behavior yang sebelumnya tersebar kini jadi tanggung jawab framework: mencegah eksekusi tumpang tindih (withoutOverlapping), menjalankan task di background, hingga memfilter berdasarkan environment.
Jadwal Hidup di dalam Kode #
Sejak Laravel 11, definisi jadwal berada di routes/console.php — berkas yang sama tempat command artisan didefinisikan lewat Artisan::command(). Berkas ini terdaftar di bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
// ...
Konsekuensinya besar: jadwal ikut di-commit, di-review, dan di-rollback bersama kode. Membandingkan dua versi jadwal cukup lewat git diff routes/console.php.
Karena jadwal adalah kode, framework bisa memeringkatnya. Perintah schedule:list menampilkan seluruh task berikut ekspresi cron mentahnya — jawaban atas masalah “tak terlihat” di atas. Contoh nyata dari sandbox dengan empat task terdefinisi:
0 * * * * ringkasan-tugas .................. Next Due: 38 minutes from now
30 2 * * * php artisan tasks:prune ............. Next Due: 6 hours from now
0 6 * * * laporan-harian ...................... Next Due: 9 hours from now
0 3 * * 1 echo "backup database mulai" ......... Next Due: 5 days from nowKondisi Eksekusi Berbasis Environment #
Jadwal juga bisa dibatasi per environment, sehingga task berat cukup aktif di production. Runner berikut mendaftarkan tasks:prune hanya untuk production, lalu membandingkan hasil eksekusinya di environment lokal:
Schedule::command('tasks:prune')
->dailyAt('02:30')
->name('prune-tugas')
->environments('production');Output yang Dihasilkan:
== lokal (APP_ENV=local):
INFO No scheduled commands are ready to run.
== environment=local (schedule:list --environment=local):
INFO No scheduled tasks have been defined.
== environment=production:
30 2 * * * php artisan tasks:prune ............. Next Due: 6 hours from nowDi environment lokal task tersebut tidak dieksekusi dan tidak tampil di schedule:list --environment=local — sementara filter --environment=production memperlihatkannya. Pengecekan environment terjadi di dalam schedule:run, bukan di crontab, sehingga satu entri cron yang sama aman dipakai di semua server.
Dengan konsep terpusat ini, pertanyaan berikutnya bersifat praktis: bagaimana cara menuliskan jadwal itu sendiri? Chapter Mendefinisikan Scheduled Tasks
membahas empat jenis task, penamaan, dan cara memverifikasinya lewat schedule:list. Rujukan resmi lengkap ada di dokumentasi Task Scheduling
.