Di lokal, job antrean berjalan senyap karena driver sync mengeksekusinya seketika. Di production dengan driver database atau redis, job menunggu diambil oleh proses queue:work yang harus hidup terus-menerus — dan di sinilah masalah klasiknya: worker yang dijalankan lewat SSH mati begitu koneksi terminal ditutup. Chapter ini membahas cara mengawasi worker dengan supervisor, mengapa worker perlu dirotasi secara berkala, dan bagaimana queue:restart menghentikan worker dengan anggun saat deploy — dengan mekanisme yang kita buktikan nyata lewat cache.
Keterbatasan Menjalankan Worker Manual #
Sebelum solusinya, mari lihat pekerjaan worker secara nyata. Kita siapkan job sederhana yang mencatat log, lalu memasukkannya ke antrean database:
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class KirimPemberitahuan implements ShouldQueue
{
use Queueable;
public function __construct(public string $nama)
{
}
public function handle(): void
{
Log::info("Pemberitahuan untuk {$this->nama} terkirim.");
}
}Dua job dikirim untuk Budi dan Siti, lalu worker menguras antrean:
php artisan queue:work --stop-when-emptyOutput yang Dihasilkan:
2026-10-13 22:19:26 App\Jobs\KirimPemberitahuan .................... RUNNING
2026-10-13 22:19:26 App\Jobs\KirimPemberitahuan ................ 4.97ms DONE
2026-10-13 22:19:26 App\Jobs\KirimPemberitahuan .................... RUNNING
2026-10-13 22:19:26 App\Jobs\KirimPemberitahuan ................ 0.73ms DONE
2026-10-13 22:19:29 Worker STOPPED Queue emptyLog membuktikan keduanya benar-benar dieksekusi: Pemberitahuan untuk Budi terkirim. dan Pemberitahuan untuk Siti terkirim.. Perintah di atas memang sengaja berhenti saat antrean kosong — padahal di production justru sebaliknya yang dibutuhkan: worker harus hidup selamanya menunggu job baru. Menjalankannya dengan nohup php artisan queue:work & lalu menutup SSH memang “bekerja”, tetapi tiga hal tidak terurus: tidak ada yang menghidupkan ulang worker saat crash atau saat server reboot, tidak ada pengelolaan beberapa worker paralel, dan output tercampur di satu berkas tanpa rotasi. Supervisor menuntaskan ketiganya.
Supervisor Menjaga Worker Tetap Hidup #
Supervisor adalah process control system yang menjalankan queue:work sebagai daemon dan bangkit otomatis saat mati. Berikut contoh tampilan konfigurasinya di /etc/supervisor/conf.d/tokobudi-worker.conf:
[program:tokobudi-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/tokobudi/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/tokobudi/storage/logs/worker.log
stopwaitsecs=3600Arahan kuncinya: autostart=true menghidupkan worker saat server boot; autorestart=true membangkitkannya lagi setiap kali proses mati; numprocs=2 menjalankan dua worker paralel dengan nama tokobudi-worker_00 dan _01; stdout_logfile menampung output ke satu berkas log; dan stopwaitsecs=3600 memberi job yang sedang berjalan waktu hingga satu jam sebelum dipaksa dimatikan. Setelah konfigurasi aktif, siklus hidupnya dikelola lewat supervisorctl — contoh tampilan:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status
sudo supervisorctl restart "tokobudi-worker:*"Rotasi Worker dan –max-jobs #
Worker queue:work adalah proses PHP yang panjang umur, dan sejarah telah membuktikan kode pihak ketiga pelan-pelan membocorkan memori — memory leak pada paket yang buruk membuat worker membengkak hingga ratusan MB lalu mati oleh OOM killer. Dua flag merawatnya. --max-time=3600 mematikan worker setelah satu jam — supervisor langsung menggantinya dengan proses segar, seperti tercantum pada blok konfigurasi di atas. --max-jobs membatasinya per jumlah job; kita buktikan dengan mengantrekan dua job lalu menjalankan worker yang berhenti setelah satu:
php artisan queue:work --max-jobs=1Output yang Dihasilkan:
2026-10-13 22:19:43 App\Jobs\KirimPemberitahuan .................... RUNNING
2026-10-13 22:19:43 App\Jobs\KirimPemberitahuan ................ 4.92ms DONE
2026-10-13 22:19:43 Worker STOPPED Maximum jobs exceededSatu job selesai, worker keluar dengan sopan, dan job kedua tinggal menunggu proses pengganti. Kombinasi keduanya memastikan memori worker selalu muda tanpa intervensi manual.
queue:restart Saat Deploy #
Kode baru yang dideploy tidak otomatis dipakai worker lama — proses PHP sudah memuat kelas lama ke memori. Momen deploy karenanya harus disertai queue:restart, dan menariknya perintah ini tidak menyentuh worker sama sekali; perintah hanya menulis sinyal ke cache store:
php artisan queue:restartOutput yang Dihasilkan:
INFO Broadcasting queue restart signal. Sinyalnya tersimpan sebagai satu baris di tabel cache — kita baca isinya langsung dari database:
laravel-cache-illuminate:queue:restart => i:1791929974;Nilai i:1791929974 adalah integer unix timestamp (2026-10-13 22:19:34 UTC) waktu sinyal dibuat. Setiap worker mengingat kapan dirinya start; saat loop antrean menemukan timestamp restart yang lebih baru dari start-nya, worker menyelesaikan job yang sedang diproses lalu keluar dengan bersih. Inilah yang membuatnya graceful: job yang sedang berjalan tidak terputus. Supervisor kemudian membangkitkan worker baru yang memuat kode hasil deploy. Potongan skrip deploy tipikal (contoh tampilan):
php artisan migrate --force
php artisan config:cache
php artisan queue:restartHorizon dan Job yang Selamat #
Untuk driver redis, paket resmi Horizon
menyediakan dasbor pemantauan worker, throughput, dan job gagal — sekaligus mengelola prosesnya sendiri lewat php artisan horizon, menggantikan konfigurasi supervisor manual di atas. Apapun pengawasnya, satu sifat tetap: job tertunda (delay), percobaan ulang, dan failed jobs
tersimpan di database/Redis, bukan di memori worker — restart apapun tidak memusnahkan antrean, sebagaimana dibahas di Queue Workers
. Hanya job yang sedang diproses saat restart yang perlu menunggu satu siklus — itupun diselesaikan dulu oleh mekanisme graceful.
Worker kini hidup di bawah pengawasan. Penghuni proses selanjutnya adalah tugas terjadwal kita — cukup satu baris cron untuk seluruh jadwal aplikasi, dibahas di Scheduled Tasks di Production .