Fix laravel queue workers stuck after deploy
Maksudur Rahman
The queue workers hang in a "processing" state after a code deploy because the PHP processes managed by Supervisor retain a stale OpCache that still references the previous .env and class map. Running php artisan queue:work --once reproduces the lock, and a simple php artisan cache:clear && supervisorctl restart all resolves it. The fix is to force OpCache invalidation on each deploy and adjust the Supervisor config to restart workers automatically.
TL;DR / Quick Summary
Verify workers are stuck with
php artisan queue:work --once.Check Supervisor logs for
SIGTERMor OpCache warnings.Add
opcache.validate_timestamps=1andopcache.revalidate_freq=0to PHP or prepend-d opcache.enable=0to the worker command.Restart workers via
supervisorctl restart laravel-queue-worker:*or runphp artisan queue:restartafter each deploy.Use the ready‑to‑paste Supervisor config snippet below.
Reproducing the Stuck State
1. Run a single‑job worker
php artisan queue:work --onceIf the command exits with Processing and never returns, the worker is stuck.
2. Simulate a deploy
git pull origin main
composer install --no-dev
php artisan config:cache
php artisan route:cache
# Do NOT restart workers yetAfter the deploy, the existing workers keep the old OpCache and remain in the processing loop.
Diagnosing with Supervisor Logs
Supervisor writes to /var/log/supervisor/laravel-queue-worker.log (path defined in the config). Look for lines such as:
2026-11-05 12:34:56,789 INFO spawned: 'laravel-queue-worker_00' with pid 12345
2026-11-05 12:35:01,102 WARN No job available; worker sleeping 3 seconds
2026-11-05 12:35:01,102 INFO exited: laravel-queue-worker_00 (exit status 0; not expected)A missing SIGTERM restart indicates the process never received the queue:restart signal.
Root Cause: OpCache Stale after .env Change
PHP 8.3 ships with OpCache enabled by default. When a deploy updates environment variables or compiled class files, the running PHP processes keep the old bytecode until the cache expires. Because Laravel’s queue workers are long‑running, they never see the new configuration, causing them to stay in a phantom "processing" state.
Verified Fixes
1. Force OpCache Invalidation on Deploy
Add the following to your deployment script after composer install:
php artisan config:clear
php artisan cache:clear
# Invalidate OpCache for the current CLI process
php -r "opcache_reset();"
php artisan queue:restartopcache_reset() clears the shared memory cache for the current PHP binary, ensuring the next worker spawn loads fresh code.
2. Adjust PHP Settings (php.ini or .user.ini)
; php.ini (PHP 8.3)
opcache.validate_timestamps=1
opcache.revalidate_freq=0These settings make OpCache check file timestamps on every request, eliminating the need for a manual reset.
3. Update Supervisor Worker Command
Create or edit ./supervisor/conf.d/laravel-worker.conf:
[program:laravel-queue-worker]
process_name=%(program_name)s_%(process_num)02d
command=php -d opcache.enable=0 /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --timeout=60
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-queue-worker.log
stopwaitsecs=360Setting -d opcache.enable=0 disables OpCache for the worker process only, guaranteeing fresh code after each deploy while keeping OpCache enabled for the web‑request PHP-FPM pool.
4. Restart Supervisor
supervisorctl reread
supervisorctl update
supervisorctl restart laravel-queue-worker:*All workers now start with a clean OpCache and will pick up new jobs immediately.
Comparison Table
Approach | OpCache Impact | Deploy Overhead | Recommended For |
|---|---|---|---|
| Uses existing OpCache, may stay stale | Minimal | Small teams, infrequent env changes |
| Real‑time validation | No extra steps | Production with frequent config changes |
Disable OpCache in worker command | No OpCache for workers | Slight CPU increase | Critical latency jobs, hot deploys |
Common Pitfalls & Troubleshooting
Workers still show "processing" after restart – Verify that the Supervisor config points to the correct
artisanpath and that thephpbinary used matches the one where you changedopcachesettings.Memory leak warnings (
php 8.3 queue worker memory leak) – Add--memory=128to the worker command or enable Laravel’s--max-jobs=1000to force periodic restarts.Redis connection errors – Ensure
REDIS_HOSTandREDIS_PASSWORDare refreshed after a deploy; runphp artisan config:clearbefore restarting workers.Supervisor not picking up config changes – Always run
supervisorctl reread && supervisorctl updateafter editing.conffiles.
FAQ
Why does
php artisan queue:work --oncehang after a deploy? Because the long‑running worker process keeps a stale OpCache that still references the old environment and class map.Do I need to disable OpCache globally? No. You can limit the change to the worker command or adjust validation settings to keep OpCache for web requests.
Will
php artisan queue:restartalone fix the issue? It works only if OpCache validation is enabled; otherwise the workers may still use cached bytecode.How can I detect this problem in CI? Add a step that runs
php artisan queue:work --onceafter a simulated deploy and asserts a non‑zero exit code.