Laravel Scheduler dengan Cron: Konfigurasi yang Benar di Ubuntu
Apa Itu Laravel Scheduler dengan Cron?
Laravel Scheduler digunakan untuk menentukan kapan sebuah task harus dijalankan, sedangkan Cron di Ubuntu bertugas memanggil Laravel Scheduler secara berkala.
Sederhananya:
Ubuntu Cron
↓
schedule:run
↓
Laravel Scheduler
↓
Task
Misalnya kita mempunyai task:
Schedule::command('report:daily')
->dailyAt('08:00');
Cron tidak perlu mengetahui bahwa task tersebut harus dijalankan pukul 08:00.
Cron cukup menjalankan:
php artisan schedule:run
setiap menit.
Laravel kemudian memeriksa:
Apakah sekarang waktunya
report:dailydijalankan?
Jika iya, Laravel menjalankan task tersebut.
Kenapa Cron Tetap Dijalankan Setiap Menit?
Ini merupakan konsep penting dalam Laravel Scheduler.
Misalnya kita memiliki:
Schedule::command('cleanup:data')
->dailyAt('02:00');
Cron tetap:
* * * * * ...
Artinya:
01:58 → schedule:run
01:59 → schedule:run
02:00 → schedule:run → cleanup:data
02:01 → schedule:run
02:02 → schedule:run
Pada pukul 02:00, Laravel melihat bahwa task sudah waktunya dijalankan.
Jadi:
Cron = pemicu
Laravel Scheduler = pengatur jadwal
Arsitektur Laravel Scheduler di Ubuntu
Pada server production, alurnya biasanya seperti ini:
Ubuntu VPS
│
├── Cron
│ ↓
│ schedule:run
│ ↓
│ Laravel Scheduler
│ ↓
├── Artisan Command
├── Job
├── Closure
└── Task lainnya
Jika task menggunakan Queue:
Cron
↓
Laravel Scheduler
↓
Queue Job
↓
Redis
↓
Queue Worker
↓
Process Job
Jadi Scheduler dan Queue Worker memiliki tanggung jawab yang berbeda.
Sebelum Mengatur Cron
Pastikan project Laravel sudah dapat dijalankan secara normal.
Masuk ke directory project:
cd /var/www/example.com
Kemudian cek:
php artisan about
Jika Laravel dapat menampilkan informasi aplikasi, lanjutkan ke konfigurasi Scheduler.
Pastikan Artisan Bisa Berjalan
Tes:
php artisan --version
Misalnya:
Laravel Framework 12.x
Jika command ini gagal, jangan lanjut mengatur Cron terlebih dahulu.
Perbaiki masalah Laravel/PHP-nya.
Buat Task Scheduler
Sebagai contoh, kita membuat Command:
php artisan make:command CleanupTemporaryData
Kemudian:
<?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;
}
}
Tes secara manual:
php artisan cleanup:temporary-data
Jika berhasil, kita dapat memasukkannya ke Scheduler.
Menambahkan Task ke Scheduler
Pada versi Laravel yang menggunakan routes/console.php, misalnya:
use Illuminate\Support\Facades\Schedule;
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00');
Untuk Laravel dengan struktur konfigurasi Scheduler yang berbeda, letakkan definisi schedule pada lokasi yang digunakan versi Laravel tersebut.
Yang penting adalah task terdaftar pada Laravel Scheduler.
Periksa Task dengan schedule:list
Jalankan:
php artisan schedule:list
Jika konfigurasi benar, task akan muncul.
Contoh:
0 2 * * * cleanup:temporary-data
Jika task tidak muncul, masalahnya belum berada di Cron.
Periksa konfigurasi Scheduler terlebih dahulu.
Tes Scheduler Secara Manual
Jalankan:
php artisan schedule:run
Command ini meminta Laravel mengecek semua task yang seharusnya dijalankan pada saat tersebut.
Jika belum waktunya:
No scheduled commands are ready to run.
Jika task memang waktunya dijalankan, Laravel akan mengeksekusinya.
Gunakan schedule:work untuk Development
Untuk testing lokal, Anda dapat menjalankan:
php artisan schedule:work
Laravel akan menjalankan scheduler secara terus-menerus.
Contohnya:
schedule:work
↓
Menunggu
↓
Task waktunya tiba
↓
Task dijalankan
↓
Menunggu lagi
Namun untuk setup production berbasis Cron, biasanya kita menggunakan:
php artisan schedule:run
yang dipanggil Cron setiap menit.
Cek Cron di Ubuntu
Pastikan Cron terpasang:
sudo apt update
Kemudian:
sudo apt install cron
Pada Ubuntu, service Cron biasanya bernama:
cron
Cek statusnya:
sudo systemctl status cron
Jika normal:
Active: active (running)
Aktifkan Cron Saat Boot
Agar Cron otomatis aktif setelah server reboot:
sudo systemctl enable cron
Jika service belum berjalan:
sudo systemctl start cron
Kemudian cek kembali:
sudo systemctl status cron
Membuat Cron Job untuk Laravel
Edit Cron user:
crontab -e
Tambahkan:
* * * * * cd /var/www/example.com && php artisan schedule:run >> /dev/null 2>&1
Ganti:
/var/www/example.com
dengan lokasi project Laravel Anda.
Inilah konfigurasi paling umum untuk Laravel Scheduler di Ubuntu.
Penjelasan Cron Expression
Bagian:
* * * * *
berarti:
│ │ │ │ │
│ │ │ │ └── Hari dalam minggu
│ │ │ └──── Bulan
│ │ └────── Hari dalam bulan
│ └──────── Jam
└────────── Menit
Semua * berarti setiap nilai.
Jadi:
* * * * *
berarti:
Jalankan setiap menit.
Penjelasan Command Cron
Baris:
* * * * * cd /var/www/example.com && php artisan schedule:run >> /dev/null 2>&1
memiliki beberapa bagian.
cd /var/www/example.com
Masuk ke directory Laravel.
&&
Jalankan command berikutnya hanya jika cd berhasil.
php artisan schedule:run
Memanggil Laravel Scheduler.
>> /dev/null
Membuang output standar.
2>&1
Mengarahkan error ke output yang sama.
Gunakan Absolute Path PHP
Salah satu masalah Cron yang sering terjadi adalah:
php: command not found
Padahal saat menjalankan:
php -v
di terminal semuanya normal.
Hal ini dapat terjadi karena environment Cron berbeda dengan shell SSH.
Cari lokasi PHP:
which php
Misalnya hasil:
/usr/bin/php
Gunakan:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
Ini lebih aman untuk production.
Periksa Versi PHP
Jalankan:
php -v
Kemudian:
which php
Pastikan versi PHP sesuai dengan kebutuhan Laravel.
Misalnya aplikasi membutuhkan PHP 8.3, tetapi Cron menjalankan PHP 8.1, Scheduler dapat gagal walaupun aplikasi melalui browser terlihat normal.
Web PHP dan CLI PHP Bisa Berbeda
Pada server Laravel, terdapat kemungkinan:
Nginx
↓
PHP-FPM 8.3
sedangkan:
Cron
↓
PHP CLI 8.1
Akibatnya:
Website → Normal
Scheduler → Error
Karena Scheduler dijalankan melalui PHP CLI.
Pastikan PHP CLI yang digunakan Cron kompatibel dengan aplikasi.
Gunakan Path Laravel yang Absolut
Daripada:
* * * * * php artisan schedule:run
gunakan:
* * * * * cd /var/www/example.com && /usr/bin/php artisan schedule:run
Atau Anda dapat menggunakan path Artisan secara langsung:
* * * * * /usr/bin/php /var/www/example.com/artisan schedule:run
Format kedua lebih eksplisit dan tidak bergantung pada cd.
Contoh Cron yang Direkomendasikan
Untuk project:
/var/www/myapp
dan PHP:
/usr/bin/php
gunakan:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /dev/null 2>&1
Format ini sederhana dan mudah dipahami.
Jangan Membuat Cron untuk Setiap Task
Misalnya Anda memiliki:
cleanup
report
sync
notification
backup
Jangan membuat:
0 2 * * * cleanup
0 8 * * * report
*/10 * * * * sync
*/5 * * * * notification
0 3 * * * backup
Jika semuanya merupakan task Laravel, lebih baik:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run
Kemudian jadwal dikelola di Laravel:
Schedule::command('cleanup')->dailyAt('02:00');
Schedule::command('report')->dailyAt('08:00');
Schedule::command('sync')->everyTenMinutes();
Schedule::command('notification')->everyFiveMinutes();
Dengan demikian konfigurasi jadwal terpusat di aplikasi.
Cek Cron yang Sudah Dibuat
Gunakan:
crontab -l
Pastikan muncul:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /dev/null 2>&1
Jika tidak muncul, Cron belum dikonfigurasi pada user tersebut.
Cron User vs Root
Perhatikan siapa yang membuat Cron.
Jika Anda menjalankan:
crontab -e
Cron tersebut berjalan sebagai user Anda.
Sedangkan:
sudo crontab -e
membuat Cron untuk root.
Ini penting karena user yang menjalankan Scheduler akan memengaruhi:
- Permission
- File yang dibuat
- Log
- Cache
- Environment
- Akses project
Sebaiknya Gunakan User Aplikasi
Misalnya aplikasi dijalankan oleh:
www-data
maka Anda dapat mempertimbangkan menjalankan Scheduler dengan user yang sesuai.
Namun jangan mengubah ownership atau user secara sembarangan.
Pastikan struktur permission aplikasi memang dirancang untuk user tersebut.
Masalah Permission pada Scheduler
Misalnya Scheduler menjalankan Command:
Storage::put('reports/daily.txt', $content);
Jika user Cron tidak memiliki permission ke:
storage/
task bisa gagal.
Periksa:
ls -la storage
dan:
ls -la bootstrap/cache
Laravel biasanya membutuhkan write access pada directory tersebut.
Jangan Menggunakan chmod -R 777
Jika menemukan:
Permission denied
hindari langsung:
chmod -R 777 /var/www/myapp
Solusi yang lebih baik adalah menentukan:
Siapa user aplikasi?
Siapa user Cron?
Directory mana yang perlu write?
Siapa owner-nya?
Kemudian atur permission secara spesifik.
Periksa Log Laravel
Jika Cron berhasil memanggil Scheduler tetapi task gagal, periksa:
tail -f /var/www/myapp/storage/logs/laravel.log
Misalnya muncul:
Permission denied
berarti task sebenarnya sudah dipanggil.
Masalahnya berada pada permission.
Jika:
RedisException
periksa Redis.
Jika:
Class not found
periksa dependency dan deployment.
Jangan Membuang Output Saat Debugging
Konfigurasi production sering menggunakan:
>> /dev/null 2>&1
Ini bagus untuk menghindari output yang tidak perlu.
Namun ketika debugging, lebih baik simpan output sementara.
Contoh:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /var/www/myapp/storage/logs/scheduler.log 2>&1
Kemudian:
tail -f /var/www/myapp/storage/logs/scheduler.log
Setelah masalah selesai, Anda dapat mengembalikan konfigurasi logging sesuai kebutuhan.
Cron Tidak Berjalan?
Jika Scheduler tidak berjalan otomatis, lakukan pengecekan dari bawah ke atas.
Cek Cron Service
sudo systemctl status cron
Cek Crontab
crontab -l
Cek PHP
which php
php -v
Cek Project
cd /var/www/myapp
php artisan --version
Cek Scheduler
php artisan schedule:list
Tes Manual
php artisan schedule:run
Jika semuanya berhasil secara manual tetapi tidak otomatis, fokus pada Cron.
Tes Cron dengan Command Sederhana
Jika masih tidak yakin Cron bekerja, buat Cron sederhana untuk menulis timestamp:
* * * * * date >> /tmp/cron-test.log
Tunggu beberapa menit, lalu:
cat /tmp/cron-test.log
Jika timestamp bertambah setiap menit:
Mon Aug 24 10:00:01 ...
Mon Aug 24 10:01:01 ...
Mon Aug 24 10:02:01 ...
berarti Cron berjalan.
Jika file tidak berubah, masalah berada pada Cron, bukan Laravel.
Tes Cron Langsung ke Laravel Log
Untuk debugging, Anda dapat menggunakan:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /tmp/laravel-scheduler.log 2>&1
Kemudian:
tail -f /tmp/laravel-scheduler.log
Ini membantu melihat apakah Laravel gagal dijalankan dari Cron.
Scheduler Tidak Menjalankan Task
Jika:
php artisan schedule:run
berhasil tetapi task tidak berjalan, periksa:
php artisan schedule:list
Misalnya task:
Schedule::command('report:daily')
->dailyAt('08:00');
tetapi saat ini pukul 10:00, tentu task tidak akan dijalankan.
Ini bukan error.
Laravel hanya menjalankan task ketika waktunya sesuai.
Timezone Sangat Penting
Misalnya Anda menginginkan:
08:00 WIB
tetapi Laravel menggunakan:
UTC
maka task dapat berjalan pada waktu yang berbeda dari yang diharapkan.
Periksa konfigurasi aplikasi.
Misalnya:
'timezone' => 'Asia/Jakarta',
Kemudian task:
Schedule::command('report:daily')
->dailyAt('08:00');
Jika aplikasi memang menggunakan waktu Indonesia, konfigurasi timezone harus konsisten.
Menentukan Timezone pada Task
Anda juga dapat menentukan timezone langsung pada task:
Schedule::command('report:daily')
->dailyAt('08:00')
->timezone('Asia/Jakarta');
Ini berguna ketika aplikasi membutuhkan jadwal berdasarkan timezone tertentu.
Periksa Waktu Server
Gunakan:
date
Untuk informasi timezone:
timedatectl
Contohnya:
Time zone: Asia/Jakarta (WIB, +0700)
Namun jangan hanya bergantung pada timezone server.
Periksa juga konfigurasi timezone Laravel.
Scheduler dan withoutOverlapping()
Task yang lama dapat bertabrakan dengan eksekusi berikutnya.
Contoh:
Schedule::command('report:generate')
->everyMinute()
->withoutOverlapping();
Jika task membutuhkan 3 menit:
00:00 → Task mulai
00:01 → Skip
00:02 → Skip
00:03 → Task selesai
00:03 → Task berikutnya dapat berjalan
Ini berguna untuk task yang tidak boleh berjalan bersamaan.
Task yang Menggunakan Queue
Jika Scheduler menjalankan Job:
Schedule::job(new GenerateReport)
->dailyAt('08:00');
maka Scheduler hanya memasukkan Job ke Queue.
Queue Worker tetap harus aktif.
Alurnya:
Cron
↓
Scheduler
↓
GenerateReport
↓
Redis
↓
Queue Worker
Jika Scheduler bekerja tetapi Worker mati:
Scheduler → berhasil
Queue → Job masuk
Worker → tidak ada
Job akan menumpuk.
Hubungan Cron dengan Supervisor
Cron dan Supervisor dapat digunakan bersamaan tetapi memiliki fungsi berbeda.
Cron:
Menjalankan Scheduler
Supervisor:
Menjaga Queue Worker tetap berjalan
Contoh:
Cron
↓
Laravel Scheduler
↓
Queue
↓
Supervisor
↓
Queue Worker
Jangan menggunakan Supervisor hanya karena aplikasi memiliki Scheduler.
Untuk Scheduler berbasis schedule:run, Cron sudah cukup pada banyak setup.
Cron vs Supervisor untuk Scheduler
Untuk Laravel Scheduler:
Cron
↓
schedule:run
adalah pendekatan sederhana dan umum.
Untuk Queue Worker:
Supervisor
↓
queue:work
lebih sesuai karena Worker adalah long-running process.
Sedangkan:
schedule:work
juga merupakan long-running process sehingga membutuhkan process manager jika digunakan sebagai mekanisme production.
Konfigurasi Production yang Disarankan
Misalnya project:
/var/www/myapp
PHP:
/usr/bin/php
Cron:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /dev/null 2>&1
Scheduler:
use Illuminate\Support\Facades\Schedule;
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00')
->withoutOverlapping();
Schedule::command('report:daily')
->dailyAt('08:00');
Schedule::job(new SyncProducts)
->everyTenMinutes();
Queue Worker:
Supervisor
↓
php artisan queue:work redis
Arsitekturnya:
Ubuntu VPS
│
┌────────┴────────┐
↓ ↓
Cron Supervisor
↓ ↓
schedule:run Queue Worker
↓ ↓
Laravel Scheduler Redis
│ ↑
└────── Job ──────┘
Setelah Mengubah .env
Scheduler dan Command dapat menggunakan konfigurasi yang sudah di-cache.
Setelah perubahan konfigurasi penting, Anda dapat menjalankan:
php artisan optimize:clear
Kemudian tes:
php artisan schedule:list
Jika menggunakan Queue Worker, restart Worker sesuai mekanisme deployment aplikasi.
Setelah Deployment
Queue Worker merupakan proses jangka panjang, sehingga perlu diperhatikan setelah deployment.
Untuk Queue Worker biasa:
php artisan queue:restart
Jika menggunakan Horizon:
php artisan horizon:terminate
Scheduler berbasis Cron tidak membutuhkan proses restart seperti Queue Worker karena Cron menjalankan schedule:run setiap menit.
Scheduler Tidak Perlu Dijalankan sebagai Daemon
Jika menggunakan Cron:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run
Anda tidak perlu menjalankan:
php artisan schedule:work
secara bersamaan.
Pilih mekanisme yang sesuai.
Untuk setup Cron:
Cron → schedule:run
Untuk setup long-running:
Process Manager → schedule:work
Jangan menjalankan keduanya tanpa alasan karena dapat membuat Anda bingung ketika debugging.
Checklist Konfigurasi Laravel Scheduler + Cron
Sebelum menyimpulkan Scheduler rusak, periksa:
[ ] Laravel dapat menjalankan artisan
[ ] Task terdaftar
[ ] php artisan schedule:list berhasil
[ ] Task dapat dijalankan manual
[ ] Cron terinstall
[ ] Cron service aktif
[ ] crontab sudah dibuat
[ ] PHP path benar
[ ] Laravel path benar
[ ] User Cron memiliki permission
[ ] Timezone benar
[ ] Queue Worker aktif jika menggunakan Job
[ ] Redis aktif jika menggunakan Redis Queue
[ ] Log dapat diperiksa
Troubleshooting Berdasarkan Gejala
| Gejala | Kemungkinan Penyebab |
|---|---|
schedule:list kosong | Konfigurasi Scheduler |
schedule:run error | Laravel/PHP/configuration |
| Manual berhasil, Cron gagal | Cron environment/PHP path |
| Task berjalan di waktu salah | Timezone |
| Task tidak muncul | Scheduler belum terdaftar |
| Command gagal | Error pada Command |
| Job masuk Queue tapi tidak diproses | Queue Worker |
php: command not found | PATH Cron |
Permission denied | User/permission |
| Cron tidak berjalan sama sekali | Service/crontab |
| Task berjalan dua kali | Multiple server/overlap |
| Scheduler berhenti setelah deploy | Environment/configuration |
Contoh Setup dari Nol
Misalnya Anda memiliki VPS Ubuntu dan project:
/var/www/blog
Pertama cek PHP:
which php
Misalnya:
/usr/bin/php
Tes Laravel:
cd /var/www/blog
/usr/bin/php artisan --version
Tes Scheduler:
/usr/bin/php artisan schedule:list
Kemudian edit Cron:
crontab -e
Tambahkan:
* * * * * /usr/bin/php /var/www/blog/artisan schedule:run >> /dev/null 2>&1
Simpan.
Cek:
crontab -l
Kemudian pastikan service:
sudo systemctl status cron
Jika semua benar, Cron akan memanggil Scheduler setiap menit.
Cara Memastikan Scheduler Benar-Benar Berjalan
Untuk debugging, ubah sementara Cron menjadi:
* * * * * /usr/bin/php /var/www/blog/artisan schedule:run >> /var/www/blog/storage/logs/scheduler.log 2>&1
Tunggu satu atau dua menit.
Kemudian:
tail -f /var/www/blog/storage/logs/scheduler.log
Jika tidak ada indikasi apa pun, tes:
/usr/bin/php /var/www/blog/artisan schedule:run
Jika manual berhasil tetapi Cron tidak, fokus pada:
Cron
User
PHP PATH
Permission
Environment
Kesimpulan
Konfigurasi Laravel Scheduler dengan Cron di Ubuntu sebenarnya cukup sederhana.
Konsep utamanya adalah:
Ubuntu Cron
↓
setiap menit
↓
php artisan schedule:run
↓
Laravel Scheduler
↓
Task yang waktunya sudah tiba
Konfigurasi Cron yang umum:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run >> /dev/null 2>&1
Sementara jadwal task dikelola di Laravel:
Schedule::command('report:daily')
->dailyAt('08:00');
Untuk task yang membutuhkan Queue:
Schedule::job(new GenerateReport)
->dailyAt('08:00');
kemudian Queue Worker dipastikan tetap berjalan menggunakan process manager seperti Supervisor.
Jika Scheduler tidak berjalan, jangan langsung mengubah konfigurasi Laravel. Periksa secara berurutan:
Cron service
↓
crontab
↓
PHP path
↓
Laravel path
↓
schedule:list
↓
schedule:run
↓
Task
↓
Queue Worker jika diperlukan
Dengan pola tersebut, Laravel Scheduler dapat berjalan secara stabil di Ubuntu VPS dan seluruh jadwal aplikasi dapat dikelola dari satu tempat tanpa harus membuat Cron terpisah untuk setiap task.

