Task Scheduling dengan Cron di Laravel

Pengingat jatuh tempo invoice yang seharusnya terkirim jam 8 pagi ternyata tidak pernah sampai, dan tidak ada yang sadar sampai pelanggan mengeluh. Atau laporan harian terkirim dua...

Task Scheduling dengan Cron di Laravel

Pengingat jatuh tempo invoice yang seharusnya terkirim jam 8 pagi ternyata tidak pernah sampai, dan tidak ada yang sadar sampai pelanggan mengeluh. Atau laporan harian terkirim dua kali karena prosesnya tumpang tindih. Masalah seperti ini hampir selalu berasal dari pekerjaan terjadwal yang tidak diatur dan tidak dipantau dengan baik.

Laravel Task Scheduler memindahkan semua jadwal ke dalam kode aplikasi dan hanya membutuhkan satu entri cron di server. Panduan ini membahas scheduler di Laravel 11: cara kerjanya, membuat command terjadwal, zona waktu, mencegah tumpang tindih, logging, pemasangan di cPanel dan VPS, serta cara men-debug jadwal yang tidak jalan.

1. Cara Kerja Scheduler

Cron di server memanggil php artisan schedule:run setiap menit. Perintah itu membaca semua definisi jadwal, mengecek mana yang jatuh tempo pada menit tersebut, lalu menjalankannya. Akibatnya:

  • Semua jadwal tercatat di version control, bukan tersebar di crontab server.
  • Developer baru cukup membaca satu file untuk tahu tugas apa saja yang berjalan.
  • Jadwal bisa diuji dan dipindah ke server lain tanpa menulis ulang crontab.
AspekBanyak entri cron manualLaravel Scheduler
Lokasi definisicrontab tiap serverroutes/console.php
Cegah proses gandaHarus buat lock sendiriwithoutOverlapping()
Zona waktu per tugasIkut zona servertimezone() per jadwal
Log dan notifikasi gagalRedirect output manualappendOutputTo(), hook onFailure()
Melihat daftar jadwalcrontab -lphp artisan schedule:list

2. Membuat Command untuk Tugas Rutin

Closure memang bisa dijadwalkan, tetapi artisan command lebih mudah diuji dan dijalankan manual. Contoh pengingat invoice yang jatuh tempo:

php artisan make:command SendInvoiceReminders
namespace App\Console\Commands;

use App\Models\Invoice;
use App\Notifications\InvoiceDueSoon;
use Illuminate\Console\Command;

class SendInvoiceReminders extends Command
{
    protected $signature = 'invoices:remind {--days=3 : Berapa hari sebelum jatuh tempo}';
    protected $description = 'Kirim pengingat untuk invoice yang akan jatuh tempo';

    public function handle(): int
    {
        $target = today()->addDays((int) $this->option('days'));
        $count = 0;

        Invoice::where('status', 'unpaid')
            ->whereDate('due_date', $target)
            ->whereNull('reminded_at')
            ->with('customer')
            ->chunkById(200, function ($invoices) use (&$count) {
                foreach ($invoices as $invoice) {
                    $invoice->customer->notify(new InvoiceDueSoon($invoice));
                    $invoice->update(['reminded_at' => now()]);
                    $count++;
                }
            });

        $this->info("Pengingat terkirim: {$count}");

        return self::SUCCESS;
    }
}

Perhatikan kolom reminded_at. Kolom ini membuat command idempoten: jika tidak sengaja berjalan dua kali, invoice yang sudah diingatkan tidak dikirimi lagi. Prinsip ini wajib untuk semua tugas terjadwal yang mengirim pesan atau mengubah uang.

3. Mendaftarkan Jadwal di routes/console.php

Di Laravel 11, app/Console/Kernel.php tidak ada lagi di proyek baru. Jadwal ditulis di routes/console.php:

use Illuminate\Support\Facades\Schedule;

Schedule::command('invoices:remind --days=3')
    ->dailyAt('08:00')
    ->timezone('Asia/Jakarta')
    ->withoutOverlapping()
    ->appendOutputTo(storage_path('logs/schedule-invoices.log'));

Schedule::command('reports:daily')
    ->weekdays()
    ->at('07:00')
    ->onFailure(function () {
        logger()->error('reports:daily gagal dijalankan');
    });

Schedule::command('model:prune')->daily();
Schedule::command('queue:prune-failed --hours=168')->weekly();

Frekuensi yang Sering Dipakai

->everyFiveMinutes();
->hourlyAt(15);              // menit ke-15 setiap jam
->dailyAt('13:00');
->weekdays()->at('09:00');
->weeklyOn(1, '06:00');      // Senin 06:00
->monthlyOn(1, '00:30');     // tanggal 1 jam 00:30
->cron('0 */2 * * *');       // ekspresi cron bebas
->environments(['production']);

4. Zona Waktu

Jam di server sering diset UTC. Jika jadwal ditulis dailyAt('08:00') tanpa zona waktu dan aplikasi memakai UTC, tugas berjalan pukul 15:00 WIB. Ada dua pilihan: set APP_TIMEZONE=Asia/Jakarta di .env agar seluruh aplikasi memakai WIB, atau tambahkan ->timezone('Asia/Jakarta') per jadwal dan biarkan database tetap UTC. Pilihan kedua lebih aman jika aplikasi Anda melayani beberapa zona waktu.

5. Memasang Cron di Server

  1. VPS: jalankan crontab -e sebagai user yang memiliki folder proyek (misalnya www-data atau user deploy), lalu tambahkan baris di bawah.
  2. cPanel: buka menu Cron Jobs, pilih "Once Per Minute", lalu isi command dengan path PHP lengkap.
  3. Pastikan versi PHP CLI sama dengan versi PHP web. Di cPanel, path-nya sering berbentuk /usr/local/bin/php atau /opt/cpanel/ea-php82/root/usr/bin/php.
* * * * * cd /home/user/aplikasi && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1

Saat development lokal, tidak perlu cron. Jalankan php artisan schedule:work yang memanggil scheduler setiap menit di terminal.

6. Mencegah Tumpang Tindih dan Tugas Lambat

  • withoutOverlapping(): jika eksekusi sebelumnya belum selesai, eksekusi berikutnya dilewati. Lock-nya kedaluwarsa setelah 24 jam secara default; tulis withoutOverlapping(30) untuk 30 menit.
  • runInBackground(): tugas lambat tidak menahan tugas lain yang dijadwalkan pada menit yang sama.
  • onOneServer(): jika aplikasi berjalan di beberapa server, tugas hanya dijalankan sekali. Butuh cache driver bersama seperti Redis atau database.
  • Pindahkan pekerjaan berat ke queue: command cukup mengirim job, lalu worker yang mengerjakan.

Di shared hosting yang tidak mengizinkan proses worker permanen, Anda bisa memproses queue lewat scheduler:

Schedule::command('queue:work --stop-when-empty --max-time=50')
    ->everyMinute()
    ->withoutOverlapping();

7. Debugging Jadwal yang Tidak Jalan

  1. Jalankan php artisan schedule:list untuk melihat jadwal dan waktu eksekusi berikutnya. Jika tugas tidak muncul, kesalahan ada di routes/console.php.
  2. Jalankan php artisan schedule:test untuk mengeksekusi satu jadwal secara manual.
  3. Jalankan command-nya langsung, misalnya php artisan invoices:remind, untuk melihat error.
  4. Cek apakah cron benar-benar memanggil Laravel: ganti sementara /dev/null dengan path file log.
  5. Cek lock yang tertinggal. Jika proses mati di tengah jalan, withoutOverlapping bisa menahan tugas sampai lock kedaluwarsa. Jalankan php artisan schedule:clear-cache.

8. Kesalahan Umum

  • Jadwal berjalan di jam yang salah: zona waktu server dan aplikasi berbeda.
  • "Could not open input file: artisan": cron tidak masuk ke folder proyek. Tambahkan cd /path/proyek &&.
  • File log tidak bisa ditulis: cron berjalan sebagai user berbeda dari web server sehingga izin folder storage bentrok.
  • Email terkirim ganda: command tidak idempoten. Tambahkan penanda seperti reminded_at.

Checklist Sebelum Produksi

  • Satu entri cron per server sudah terpasang dan memakai PHP CLI yang benar.
  • Setiap tugas punya zona waktu yang jelas.
  • Tugas yang mengirim pesan atau mengubah data bersifat idempoten.
  • Tugas panjang memakai withoutOverlapping() atau dipindah ke queue.
  • Output dicatat ke log dan ada notifikasi saat gagal.
  • schedule:list sudah dicek setelah deploy.

Untuk tugas yang memakan waktu lama, baca queue dan job di Laravel. Langkah memasang cron di hosting juga dibahas di panduan deploy Laravel ke hosting dan deploy aplikasi Laravel ke shared hosting.

Ingin langsung mempraktikkannya?

Buat akun gratis untuk mengunduh contoh aplikasi Laravel, menyimpan tutorial favorit, dan mencoba kursus. Gratis, bisa daftar dengan akun Google.

Daftar Gratis Lihat katalog aplikasi
Yudhi
Ditulis oleh
Yudhi
Founder & Lead Developer, GudangCode

Yudhi adalah founder GudangCode dan developer Laravel dengan pengalaman membangun puluhan sistem informasi bisnis siap pakai — mulai dari POS, HRIS, hingga aplikasi manajemen. Ia menulis panduan dan artikel di GudangCode untuk membantu developer Indonesia menjalankan, memahami, dan men-deploy source code Laravel dengan benar.

Laravel PHP MySQL Sistem Informasi Bisnis
Lihat semua artikel Yudhi
Task Scheduling Laravel Programming & Laravel
Bacaan Paling Dicari
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →