Queue Worker Tuning
debt(d8/e3/b5/t7)
Closest to 'silent in production until users hit it' (d9), but Horizon/Datadog monitoring can surface memory growth and queue depth issues if configured, so d8. Without monitoring, memory leaks and queue backups only manifest at 3am when servers OOM.
Closest to 'simple parameterised fix' (e3) — quick_fix is adding --timeout, --max-jobs, --sleep flags to the worker command (supervisor config or Horizon config), a parameterised change in one place.
Closest to 'persistent productivity tax' (b5) — queue worker configuration is load-bearing infrastructure that shapes how all async work behaves; getting it wrong affects every job type, and tuning decisions (queue priorities, worker pools) persist across the codebase.
Closest to 'serious trap' (t7) — misconception explicitly says devs assume workers run forever without issues, contradicting how short-lived request handlers work in PHP; the long-running PHP process model is genuinely different from request/response and traps developers used to web context.
Also Known As
TL;DR
Explanation
PHP queue workers are long-running processes — unlike PHP-FPM requests they do not restart after each job. Memory leaks accumulate over time. Key configuration: --max-jobs (restart after N jobs, prevents memory leak), --max-time (restart after N seconds), --memory (restart if memory exceeds limit), --timeout (kill hung jobs), --tries (retry failed jobs), --backoff (delay between retries). Supervisor keeps workers alive and respawns crashed workers. Horizon provides per-queue concurrency, job metrics, and real-time dashboard for Laravel.
Common Misconception
Why It Matters
Common Mistakes
- No --max-jobs limit — worker runs until it exhausts memory, then crashes and all queued jobs time out.
- Timeout shorter than the longest expected job — jobs are killed mid-execution, causing partial processing.
- Not using --sleep for empty queues — workers spin at 100% CPU when the queue is empty without a sleep delay.
- One worker process for all queues — high-priority queues blocked behind slow bulk jobs.
Code Examples
# Supervisor config — no memory limit, no max-jobs:
[program:worker]
command=php artisan queue:work
autostart=true
autorestart=true
# Worker runs indefinitely
# Memory grows from 50MB to 2GB over 3 days
# Server OOM kills worker
# All jobs fail until Supervisor restarts it
# Supervisor — with proper limits:
[program:worker-default]
command=php artisan queue:work --queue=default,low
--max-jobs=1000 --max-time=3600
--memory=256 --timeout=90
--sleep=3 --tries=3 --backoff=10
process_num=4 ; 4 concurrent workers
autostart=true
autorestart=true
stopwaitsecs=120 ; Allow jobs to finish gracefully
[program:worker-critical]
command=php artisan queue:work --queue=critical
--max-jobs=500 --memory=128 --timeout=30
process_num=2