Laravel Task Scheduling: Cara Menjalankan Command Secara Berkala
Apa Itu Laravel Task Scheduling?
Laravel Task Scheduling adalah fitur Laravel yang digunakan untuk menjalankan task secara otomatis berdasarkan jadwal tertentu.
Misalnya aplikasi membutuhkan proses:
- Menghapus data sementara setiap hari
- Mengirim laporan setiap pagi
- Membersihkan cache secara berkala
- Memproses data setiap beberapa menit
- Menjalankan Artisan Command
- Menjalankan Queue Job berdasarkan jadwal
- Membersihkan file lama
- Mengirim notifikasi otomatis
Tanpa Laravel Scheduler, kita biasanya perlu membuat banyak konfigurasi Cron secara terpisah.
Dengan Laravel Scheduler, jadwal dapat dikelola dari aplikasi Laravel.
Contohnya:
Laravel Scheduler
↓
Setiap menit
↓
Periksa task
↓
Task sesuai jadwal?
↓
Jalankan
Kenapa Menggunakan Laravel Scheduler?
Tanpa Scheduler, kita mungkin membuat banyak Cron Job:
Cron 1 → cleanup
Cron 2 → report
Cron 3 → notification
Cron 4 → backup
Cron 5 → sync data
Konfigurasi tersebut dapat menjadi sulit dipelihara.
Dengan Laravel Scheduler:
Cron
↓
Laravel Scheduler
├── cleanup
├── report
├── notification
├── backup
└── sync data
Server hanya perlu memanggil Laravel Scheduler secara berkala, sedangkan jadwal task dikelola di aplikasi.
Contoh Penggunaan Task Scheduling
Misalnya kita ingin menjalankan Command setiap hari:
Setiap hari pukul 01:00
↓
Laravel Scheduler
↓
cleanup:temporary-data
Atau:
Setiap 5 menit
↓
Sync data
Atau:
Setiap hari pukul 08:00
↓
Kirim laporan
Laravel Scheduler dan Cron
Laravel Scheduler tetap membutuhkan mekanisme dari server untuk memicunya.
Pada Linux/Ubuntu, biasanya digunakan Cron.
Konsepnya:
Ubuntu Cron
↓
Laravel Scheduler
↓
Task
Cron bertugas memanggil Scheduler.
Laravel kemudian menentukan task mana yang harus dijalankan.
Membuat Artisan Command
Salah satu cara yang umum adalah membuat Artisan Command.
Misalnya:
php artisan make:command CleanupTemporaryData
Laravel akan membuat file Command di:
app/Console/Commands/CleanupTemporaryData.php
Contoh sederhana:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class CleanupTemporaryData extends Command
{
protected $signature = 'cleanup:temporary-data';
protected $description = 'Membersihkan data sementara';
public function handle()
{
$this->info('Cleanup selesai.');
return self::SUCCESS;
}
}
Sekarang kita memiliki Command:
php artisan cleanup:temporary-data
Menjalankan Command Secara Manual
Sebelum memasukkannya ke Scheduler, selalu tes Command secara manual.
php artisan cleanup:temporary-data
Jika menghasilkan:
Cleanup selesai.
berarti Command dapat dijalankan.
Ini penting karena jika Command sendiri error, Scheduler juga tidak akan menyelesaikan task tersebut.
Menentukan Jadwal Task
Setelah Command siap, kita dapat memasukkannya ke Scheduler.
Pada versi Laravel tertentu, konfigurasi scheduling dapat berada di:
routes/console.php
Contoh:
use Illuminate\Support\Facades\Schedule;
Schedule::command('cleanup:temporary-data')
->daily();
Artinya Command:
php artisan cleanup:temporary-data
akan dijalankan satu kali setiap hari.
Lokasi konfigurasi Scheduler dapat berbeda tergantung versi Laravel. Karena itu, gunakan struktur scheduling yang sesuai dengan versi Laravel yang digunakan project.
Menjalankan Task Setiap Menit
Jika ingin menjalankan Command setiap menit:
Schedule::command('cleanup:temporary-data')
->everyMinute();
Konsepnya:
00:00 → Run
00:01 → Run
00:02 → Run
00:03 → Run
...
Namun jangan menggunakan everyMinute() untuk task berat tanpa mempertimbangkan resource server.
Menjalankan Task Setiap Lima Menit
Gunakan:
Schedule::command('cleanup:temporary-data')
->everyFiveMinutes();
Task akan dijalankan:
10:00
10:05
10:10
10:15
...
Interval lain yang tersedia dapat digunakan sesuai kebutuhan aplikasi.
Menjalankan Task Setiap Jam
Untuk task setiap jam:
Schedule::command('cleanup:temporary-data')
->hourly();
Misalnya:
01:00
02:00
03:00
04:00
...
Ini cocok untuk proses seperti sinkronisasi data yang tidak membutuhkan interval terlalu pendek.
Menjalankan Task Setiap Hari
Gunakan:
Schedule::command('cleanup:temporary-data')
->daily();
Secara default, task akan dijalankan sekali sehari pada waktu yang ditentukan oleh scheduler Laravel.
Jika ingin menentukan jam tertentu, gunakan:
Schedule::command('cleanup:temporary-data')
->dailyAt('01:00');
Artinya:
Setiap hari
pukul 01:00
Menjalankan Task Setiap Hari pada Jam Tertentu
Contoh mengirim laporan setiap pukul 08:00:
Schedule::command('report:daily')
->dailyAt('08:00');
Contoh lain:
Schedule::command('backup:database')
->dailyAt('02:00');
Ini cocok untuk task yang ingin dijalankan pada waktu tertentu.
Menjalankan Task Mingguan
Untuk task setiap minggu:
Schedule::command('report:weekly')
->weekly();
Jika ingin menentukan hari dan jam, gunakan konfigurasi yang sesuai dengan kebutuhan.
Misalnya task laporan setiap Senin pukul 08:00:
Schedule::command('report:weekly')
->weeklyOn(1, '08:00');
Pada konfigurasi tersebut:
1 = Monday
08:00 = waktu eksekusi
Menjalankan Task Bulanan
Untuk task setiap bulan:
Schedule::command('report:monthly')
->monthly();
Misalnya laporan bulanan:
Schedule::command('report:monthly')
->monthlyOn(1, '08:00');
Artinya task dijadwalkan pada tanggal 1 setiap bulan pukul 08:00.
Menjalankan Job dengan Scheduler
Scheduler tidak hanya dapat menjalankan Artisan Command.
Kita juga dapat menjalankan Queue Job.
Misalnya:
Schedule::job(new SendDailyReport)
->dailyAt('08:00');
Alurnya:
Scheduler
↓
08:00
↓
SendDailyReport
↓
Queue
↓
Queue Worker
Pendekatan ini sangat berguna untuk task yang membutuhkan proses background.
Scheduler dan Queue Bisa Digabungkan
Misalnya setiap jam aplikasi harus melakukan sinkronisasi data.
Kita bisa menggunakan:
Schedule::job(new SyncProducts)
->hourly();
Kemudian Queue Worker yang memproses Job:
Cron
↓
Scheduler
↓
SyncProducts Job
↓
Redis
↓
Queue Worker
↓
Process
Dengan demikian Scheduler tidak harus melakukan seluruh pekerjaan secara langsung.
Scheduler vs Queue
Keduanya memiliki fungsi berbeda.
Scheduler menentukan:
Kapan task dijalankan?
Queue menentukan:
Bagaimana pekerjaan diproses di background?
Contoh:
Scheduler
↓
Setiap pukul 08:00
↓
Dispatch Job
↓
Queue
↓
Worker
Ini merupakan pola yang sangat berguna pada aplikasi Laravel production.
Menjalankan Closure
Laravel Scheduler juga dapat menjalankan Closure.
Contoh:
Schedule::call(function () {
logger()->info('Scheduler berjalan');
})->daily();
Ini cocok untuk task sederhana.
Namun untuk logic yang kompleks, lebih baik gunakan Artisan Command atau Job agar kode tetap mudah dirawat.
Menjalankan Shell Command
Scheduler juga dapat menjalankan command melalui:
Schedule::exec('php /path/to/script.php')
->daily();
Namun hati-hati ketika menjalankan command sistem.
Pastikan:
- Path benar
- User memiliki permission
- Command aman
- Environment sesuai
- Output/error dapat dilacak
Untuk logic yang merupakan bagian dari aplikasi Laravel, Artisan Command biasanya lebih mudah dipelihara.
Memeriksa Task yang Terdaftar
Laravel menyediakan command:
php artisan schedule:list
Command tersebut dapat membantu melihat task yang terdaftar dan jadwalnya.
Contohnya Anda mungkin melihat:
0 1 * * * cleanup:temporary-data
0 8 * * * report:daily
*/5 * * * * sync:products
Jika task tidak muncul di schedule:list, periksa konfigurasi Scheduler terlebih dahulu.
Cara Menjalankan Scheduler Secara Lokal
Untuk development, kita dapat menjalankan:
php artisan schedule:work
Laravel akan menjalankan scheduler secara terus-menerus.
Konsepnya:
schedule:work
↓
Menunggu jadwal
↓
Task waktunya tiba
↓
Task dijalankan
↓
Menunggu lagi
Ini sangat berguna untuk testing.
schedule:work vs Cron
Ada dua konsep penting.
schedule:work
Laravel menjalankan proses scheduler yang terus aktif.
php artisan schedule:work
Cron
Server menjalankan Scheduler setiap menit:
* * * * * cd /var/www/example.com && php artisan schedule:run
Untuk production, Cron biasanya menjadi pilihan sederhana karena Scheduler Laravel tidak perlu berjalan sebagai long-running process.
Konfigurasi Cron di Ubuntu
Edit Cron user:
crontab -e
Kemudian tambahkan:
* * * * * cd /var/www/example.com && php artisan schedule:run >> /dev/null 2>&1
Artinya setiap menit Ubuntu akan menjalankan:
php artisan schedule:run
Laravel kemudian menentukan task mana yang waktunya sudah tiba.
Kenapa Cron Hanya Setiap Menit?
Pertanyaan umum adalah:
Kalau task dijalankan setiap hari, kenapa Cron tetap setiap menit?
Karena Laravel Scheduler yang menentukan apakah task harus dieksekusi.
Misalnya:
00:00 → schedule:run
00:01 → schedule:run
00:02 → schedule:run
...
08:00 → schedule:run
↓
report:daily
Jadi Cron hanya bertugas memanggil Scheduler.
Pastikan Path Laravel Benar
Contoh:
* * * * * cd /var/www/example.com && php artisan schedule:run >> /dev/null 2>&1
Pastikan:
/var/www/example.com
merupakan directory project Laravel yang benar.
Cek:
cd /var/www/example.com
ls
Harus terdapat file seperti:
artisan
composer.json
app/
config/
routes/
storage/
Gunakan Absolute Path PHP pada Cron
Jika Cron tidak menemukan php, gunakan absolute path.
Cari:
which php
Misalnya:
/usr/bin/php
Cron dapat ditulis:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Ini dapat menghindari masalah PATH yang berbeda antara shell biasa dan Cron.
Pastikan Cron Service Berjalan
Di Ubuntu:
sudo systemctl status cron
Jika belum berjalan:
sudo systemctl start cron
Dan agar otomatis aktif setelah reboot:
sudo systemctl enable cron
Nama service dapat berbeda pada distribusi Linux tertentu, tetapi pada Ubuntu umumnya menggunakan cron.
Cek Cron yang Terpasang
Gunakan:
crontab -l
Pastikan terdapat:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Jika tidak ada, Scheduler tidak akan dipanggil oleh Cron.
Masalah Scheduler Tidak Berjalan
Jika task tidak dijalankan, jangan langsung menyalahkan Laravel.
Periksa urutannya:
Cron
↓
schedule:run
↓
Scheduler
↓
Task
↓
Command/Job
Mulai dari Cron.
Tes schedule:run Secara Manual
Masuk ke project:
cd /var/www/example.com
Kemudian:
php artisan schedule:run
Jika task sedang waktunya dijalankan, Laravel akan menjalankannya.
Jika tidak ada task yang harus dijalankan, Anda mungkin tidak melihat output yang berarti.
Untuk debugging, gunakan:
php artisan schedule:list
Gunakan schedule:list untuk Debugging
Misalnya terdapat:
Schedule::command('report:daily')
->dailyAt('08:00');
jalankan:
php artisan schedule:list
Periksa apakah task muncul.
Jika tidak muncul:
Scheduler configuration
perlu diperiksa.
Jika muncul tetapi tidak berjalan:
Cron
Environment
Timezone
Command
perlu diperiksa.
Perhatikan Timezone
Timezone merupakan salah satu penyebab Scheduler berjalan pada waktu yang tidak sesuai.
Misalnya Anda mengharapkan:
08:00 WIB
tetapi server menggunakan:
UTC
Task bisa berjalan pada waktu yang berbeda dari ekspektasi.
Periksa konfigurasi aplikasi:
'timezone' => 'Asia/Jakarta',
di konfigurasi Laravel sesuai kebutuhan aplikasi.
Anda juga dapat menentukan timezone pada task tertentu:
Schedule::command('report:daily')
->dailyAt('08:00')
->timezone('Asia/Jakarta');
Ini berguna ketika server atau aplikasi melayani timezone tertentu.
Timezone Server vs Timezone Laravel
Ada dua hal yang perlu dibedakan:
Server timezone
dan:
Laravel application timezone
Misalnya server menggunakan:
UTC
tetapi aplikasi menggunakan:
Asia/Jakarta
Ini tidak selalu menjadi masalah selama konfigurasi Scheduler dipahami dengan benar.
Yang penting adalah menentukan timezone yang memang diinginkan aplikasi.
Hindari Task yang Berjalan Tumpang Tindih
Misalnya sebuah task dijadwalkan:
Schedule::command('report:generate')
->everyMinute();
tetapi prosesnya membutuhkan:
3 menit
Maka task berikutnya berpotensi mulai sebelum task sebelumnya selesai.
Konsepnya:
00:00 → Task A mulai
00:01 → Task B mulai
00:02 → Task C mulai
00:03 → Task A selesai
Jika task tidak dirancang untuk berjalan paralel, ini dapat menyebabkan masalah.
Gunakan withoutOverlapping()
Untuk mencegah task yang sama berjalan bersamaan:
Schedule::command('report:generate')
->everyMinute()
->withoutOverlapping();
Konsepnya:
Task A
↓
Sedang berjalan
↓
Task berikutnya?
↓
Skip karena masih running
Ini sangat berguna untuk task yang membutuhkan waktu lama.
Mengatur Lock withoutOverlapping
Anda dapat menentukan berapa lama lock dianggap valid.
Contoh:
Schedule::command('report:generate')
->everyMinute()
->withoutOverlapping(10);
Nilainya perlu disesuaikan dengan karakteristik task.
Jika task biasanya membutuhkan 5 menit, timeout lock jangan terlalu pendek.
Menjalankan Task Hanya di Environment Tertentu
Misalnya task tertentu hanya boleh berjalan di production.
Gunakan:
Schedule::command('cleanup:temporary-data')
->daily()
->environments(['production']);
Dengan begitu task tidak dijalankan pada environment lain seperti development atau testing.
Ini sangat berguna agar task production tidak sengaja dijalankan di lokal.
Menjalankan Task di Server Tertentu
Pada aplikasi yang menggunakan beberapa server, perlu berhati-hati.
Misalnya:
Server A
Server B
Server C
Jika semuanya menjalankan Scheduler:
A → schedule:run
B → schedule:run
C → schedule:run
task tertentu bisa dijalankan lebih dari sekali.
Untuk kebutuhan seperti ini, Laravel menyediakan mekanisme seperti:
->onOneServer()
dengan kondisi dan konfigurasi cache yang sesuai.
Contohnya:
Schedule::command('report:daily')
->daily()
->onOneServer();
Gunakan ini ketika Scheduler memang berjalan pada beberapa server dan Anda ingin task hanya dieksekusi oleh satu server.
Task Scheduling pada Multiple Server
Tanpa mekanisme koordinasi:
Server A → Task
Server B → Task
Server C → Task
Akibatnya satu task dapat dieksekusi tiga kali.
Dengan onOneServer() dan cache/lock yang dikonfigurasi dengan benar:
Server A ─┐
Server B ─┼→ Lock → Satu server menjalankan task
Server C ─┘
Ini penting untuk aplikasi yang sudah menggunakan load balancing atau horizontal scaling.
Mengirim Output Task ke File
Untuk command tertentu, Anda dapat mengarahkan output ke file.
Contoh:
Schedule::command('report:daily')
->dailyAt('08:00')
->appendOutputTo(storage_path('logs/report-scheduler.log'));
Dengan begitu, output Command dapat diperiksa ketika terjadi masalah.
Mengirim Output ke Email
Untuk kebutuhan tertentu, output task dapat diarahkan ke email menggunakan fitur scheduling yang sesuai.
Namun untuk production, biasanya lebih baik menggunakan logging dan monitoring yang terstruktur daripada mengirim semua output task melalui email.
Menjalankan Task Setelah Task Lain
Terkadang kita memiliki dependensi:
Import Data
↓
Process Data
↓
Generate Report
Scheduler menyediakan kemampuan untuk mengatur alur tertentu, tetapi sebaiknya task yang saling bergantung secara kuat dipertimbangkan untuk dibuat sebagai Job chain atau workflow yang lebih jelas.
Jangan membuat dependency yang kompleks hanya melalui banyak jadwal terpisah jika urutannya sangat penting.
Scheduler untuk Database Cleanup
Contoh task:
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00');
Command:
public function handle()
{
TemporaryData::where(
'created_at',
'<',
now()->subDays(7)
)->delete();
return self::SUCCESS;
}
Dengan demikian data yang lebih lama dari 7 hari dapat dibersihkan secara otomatis.
Tetap pertimbangkan ukuran data dan beban database sebelum melakukan operasi delete dalam jumlah besar.
Scheduler untuk Mengirim Laporan
Misalnya:
Schedule::job(new SendDailyReport)
->dailyAt('08:00');
Alurnya:
08:00
↓
Scheduler
↓
SendDailyReport
↓
Redis
↓
Queue Worker
↓
Email
Ini merupakan pola yang lebih baik daripada membuat Scheduler melakukan proses berat secara langsung.
Scheduler untuk Sinkronisasi API
Misalnya aplikasi perlu mengambil data dari API eksternal setiap 10 menit:
Schedule::job(new SyncExternalProducts)
->everyTenMinutes();
Kemudian:
Scheduler
↓
SyncExternalProducts
↓
Queue
↓
Worker
↓
External API
↓
Database
Jika API lambat atau mengalami masalah, Queue Worker dapat menangani proses tersebut tanpa membuat Scheduler menjadi terlalu berat.
Jangan Menaruh Proses Berat di Scheduler
Hindari:
Schedule::call(function () {
// proses jutaan data
// request API dalam jumlah besar
// generate report besar
})->daily();
Lebih baik:
Scheduler
↓
Dispatch Job
↓
Queue
↓
Worker
↓
Proses Berat
Dengan pola tersebut Scheduler hanya bertugas memicu pekerjaan.
Scheduler dan Redis
Jika aplikasi sudah menggunakan Redis untuk Queue, Redis juga dapat membantu beberapa fitur scheduling yang membutuhkan cache/lock.
Namun pastikan konfigurasi Redis benar.
Misalnya:
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
Jika menggunakan withoutOverlapping() atau onOneServer(), konfigurasi cache yang digunakan aplikasi harus mendukung mekanisme lock yang diperlukan.
Scheduler Tidak Berjalan Setelah Deploy
Jika Scheduler bekerja di lokal tetapi tidak di production, periksa:
Cron
PHP path
Project path
Environment
Timezone
Permission
Configuration cache
Mulai dengan:
php artisan schedule:list
Kemudian:
php artisan schedule:run
Jika manual berhasil, kemungkinan masalah berada pada Cron atau environment server.
Masalah Cron Menggunakan PHP yang Berbeda
Misalnya terminal:
php -v
menghasilkan:
PHP 8.3
tetapi Cron menjalankan:
PHP 8.1
Task bisa gagal karena dependency atau syntax yang tidak kompatibel.
Gunakan absolute path:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Pastikan /usr/bin/php memang versi yang benar.
Masalah Permission pada Cron
Cron dapat berjalan menggunakan user yang berbeda dari PHP-FPM.
Misalnya:
Cron
↓
www-data
tetapi file project dimiliki:
deploy
Jika Scheduler menjalankan Command yang membutuhkan write access, dapat muncul:
Permission denied
Periksa user Cron:
whoami
dan permission directory Laravel.
Periksa Log Laravel
Jika task berjalan tetapi Command mengalami error:
tail -f storage/logs/laravel.log
Ini penting untuk membedakan:
Scheduler tidak dipanggil
dengan:
Scheduler dipanggil tetapi task error
Cara Debugging yang Sistematis
Jika task tidak berjalan, gunakan urutan:
1. Task terdaftar?
↓
schedule:list
2. Scheduler bisa berjalan?
↓
schedule:run
3. Command/Job bisa berjalan?
↓
Jalankan manual
4. Cron aktif?
↓
systemctl status cron
5. Cron entry ada?
↓
crontab -l
6. PHP path benar?
↓
which php
7. Timezone benar?
↓
Laravel config
8. Permission benar?
↓
storage/bootstrap/cache
Urutan ini membantu mempersempit sumber masalah tanpa mengubah banyak konfigurasi sekaligus.
Checklist Laravel Task Scheduling di Ubuntu
Untuk production, checklist sederhananya:
[ ] Task sudah dibuat
[ ] Task muncul di schedule:list
[ ] Command/Job berhasil dijalankan manual
[ ] Laravel configuration benar
[ ] Timezone sesuai
[ ] Cron terinstall
[ ] Cron service aktif
[ ] Crontab sudah dikonfigurasi
[ ] Absolute path PHP benar
[ ] Project path benar
[ ] Permission benar
[ ] Queue Worker aktif jika menggunakan Job
[ ] Log dapat diperiksa
[ ] Overlapping dipertimbangkan
Contoh Setup Lengkap
Misalnya kita memiliki Command:
php artisan cleanup:temporary-data
Scheduler:
use Illuminate\Support\Facades\Schedule;
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00')
->withoutOverlapping();
Cron Ubuntu:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Alurnya:
Setiap menit
↓
Ubuntu Cron
↓
php artisan schedule:run
↓
Laravel mengecek jadwal
↓
Apakah pukul 02:00?
↓
Ya
↓
cleanup:temporary-data
Jika pukul 10:00:
Cron
↓
schedule:run
↓
Bukan waktu task
↓
Tidak menjalankan cleanup
Contoh Scheduler dengan Queue
Untuk proses yang lebih berat:
Schedule::job(new GenerateDailyReport)
->dailyAt('08:00')
->withoutOverlapping();
Kemudian:
08:00
↓
Scheduler
↓
GenerateDailyReport
↓
Redis
↓
Queue Worker
↓
Generate Report
Supervisor menjaga Queue Worker:
Supervisor
↓
queue:work
Sehingga ketiga komponen bekerja bersama:
Cron
↓
Laravel Scheduler
↓
Redis Queue
↓
Supervisor
↓
Queue Worker
Kesimpulan
Laravel Task Scheduling digunakan untuk menjalankan Command, Job, Closure, atau task lain secara otomatis berdasarkan jadwal.
Konsep utamanya sederhana:
Cron
↓
Laravel Scheduler
↓
Task
Pada aplikasi yang menggunakan Queue:
Cron
↓
Scheduler
↓
Queue Job
↓
Redis
↓
Queue Worker
Untuk membuat Scheduler berjalan di Ubuntu, konfigurasi yang paling umum adalah:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Kemudian jadwal didefinisikan di Laravel, misalnya:
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00');
Untuk task yang membutuhkan waktu lama, pertimbangkan menggunakan:
Schedule::job(new GenerateDailyReport)
->dailyAt('08:00')
->withoutOverlapping();
Dengan pola tersebut, Cron bertugas memicu Scheduler, Scheduler menentukan kapan task dijalankan, sedangkan Queue Worker menangani pekerjaan background yang berat.
Untuk production, jangan lupa memperhatikan timezone, permission, PHP path, Cron service, Queue Worker, Redis, dan logging. Hal-hal tersebut merupakan penyebab umum ketika Laravel Scheduler terlihat sudah dikonfigurasi tetapi task sebenarnya tidak pernah berjalan.

