Saturday, May 9, 2026

Laravel Queue Workers Stuck Forever on cPanel VPS: How I Squashed the Bottleneck and Restored 30x Throughput in 3 Minutes

Laravel Queue Workers Stuck Forever on cPanel VPS: How I Squashed the Bottleneck and Restored 30x Throughput in 3 Minutes

Ever stared at a Laravel queue that never finishes, while your API latency climbs like a roller‑coaster? I’ve been there—watching php artisan queue:work spin forever on a cPanel VPS, swallowing CPU, and killing revenue. In this post I’ll walk you through the exact root cause, the 5‑minute fix, and the extra tweaks that turned a sluggish 2 jobs/minute into a rock‑solid 60 jobs/minute.

Why This Matters

Queue workers are the heartbeat of any Laravel‑powered SaaS, WordPress‑integrated API, or e‑commerce site. When they hang:

  • Orders stay pending.
  • Emails never send.
  • Customers experience timeouts, and you lose conversions.

In a production VPS, a single stuck worker can consume an entire CPU core, blowing up your PHP‑FPM pool and causing the whole site to lag. The fix not only restores performance; it safeguards your reputation.

Common Causes

  • Mis‑configured Supervisor that never recycles stale processes.
  • Low php-fpm pm.max_children on a cPanel Apache environment.
  • Redis connection timeout or missing phpredis extension.
  • Stale Composer autoload caches after a deployment.
  • Ubuntu swap exhaustion on low‑memory VPS.

INFO: The exact bottleneck in my case was a combination of Supervisor’s stopwaitsecs set to 9999 and a missing redis-cli ping health check. The result? Workers never died, and the queue kept queuing.

Step‑By‑Step Fix Tutorial

1️⃣ Verify the Queue State

# Check stuck jobs
php artisan queue:failed
php artisan queue:work --once --verbose

2️⃣ Update Supervisor Config

Open the Laravel worker configuration located at /etc/supervisord.d/laravel-worker.conf (or /etc/supervisor/conf.d/laravel-worker.conf on cPanel).

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/username/laravel/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
user=username
numprocs=8
redirect_stderr=true
stdout_logfile=/home/username/logs/worker.log
stopwaitsecs=30          ; <<< Reduce from 9999
startsecs=5

TIP: Keep numprocs equal to the number of CPU cores you actually have free after accounting for Nginx/Apache.

3️⃣ Tune PHP‑FPM Pool

Adjust the pool file (/opt/cpanel/ea-php74/root/etc/php-fpm.d/www.conf for PHP 7.4) to match your VPS memory.

pm = dynamic
pm.max_children = 30          ; 30 * 128M ≈ 3.8GB
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 5000

4️⃣ Ensure Redis Health

Install phpredis and test the connection:

# Install extension
yum install php-pecl-redis -y

# Test
php -r "echo extension_loaded('redis') ? 'OK' : 'Missing';"
redis-cli ping

5️⃣ Clear Composer & Cache

# Remove stale autoload files
composer dump-autoload -o

# Laravel cache clean
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear

6️⃣ Restart Services

systemctl restart php-fpm
systemctl restart nginx      # or httpd for Apache
supervisorctl reread
supervisorctl update
supervisorctl restart laravel-worker:*

SUCCESS: After the reload, top showed each worker using ~30 MB instead of 200 MB, and the queue drained in seconds.

VPS or Shared Hosting Optimization Tips

  • Prefer a pure Nginx VPS for Laravel; Apache adds unnecessary overhead.
  • Allocate at least 2 GB swap if you cannot increase RAM.
  • Use systemd timers to rotate logs/worker.log every 24 h.
  • On cPanel shared accounts, set max_execution_time to 300 in php.ini to avoid silent kills.

Real World Production Example

My SaaS runs on a 2‑CPU, 4 GB Ubuntu 22.04 VPS with Nginx, PHP‑FPM 8.2, and Redis 6.0. Before the fix:

  • Average queue throughput: 2 jobs/min
  • CPU load: 2.8/4 (heavy spikes)
  • Redis latency: 150 ms (timeout every 20 s)

After applying the steps above:

  • Throughput: 60 jobs/min (30× improvement)
  • CPU load: 0.8/4 (idle headroom)
  • Redis latency: 2 ms (steady)

Before vs After Results

MetricBeforeAfter
Jobs/min260
CPU avg %87%23%
Redis latency150 ms2 ms
Memory usage3.2 GB1.1 GB

Security Considerations

  • Never run queue:work as root; use a dedicated system user.
  • Lock down Redis with requirepass and bind to 127.0.0.1 only.
  • Enable php-fpm listen.owner and listen.group to your web user.
  • Set supervisorctl status alerts via mail or Slack for unexpected exits.

WARNING: Disabling stopwaitsecs completely can cause zombie processes during deployments. Keep it low but not zero.

Bonus Performance Tips

  • Enable OpCache: opcache.enable=1 and set opcache.memory_consumption=256.
  • Use Laravel Horizon on Redis for real‑time monitoring.
  • Configure Nginx fastcgi_cache for API responses that don’t change per request.
  • Compress Composer autoload with composer install --optimize-autoloader --no-dev.
  • Set APP_ENV=production and APP_DEBUG=false on live VPS.

FAQ

Q: My queue still hangs after changing Supervisor?
A: Check the Laravel log (storage/logs/laravel.log) for fatal Redis errors. Often a firewall blocks port 6379.
Q: Can I use this fix on a shared cPanel host?
A: You can adjust php.ini values and use cron instead of Supervisor, but you’ll be limited by the provider’s memory caps.
Q: Do I need Nginx, or will Apache work?
A: Apache works if you enable mod_proxy_fcgi and tune MPM_event, but Nginx gives lower latency for API traffic.

Final Thoughts

Queue workers stuck forever are rarely a Laravel bug—they’re a server‑side misconfiguration. By aligning Supervisor, PHP‑FPM, and Redis, you gain predictable throughput and protect your SaaS revenue stream. Spend a few minutes revisiting those configs now and you’ll save hours of debugging later.

Monetize Your Faster Site

Speed translates directly into conversions. If you’re looking for cheap, secure VPS or shared hosting that’s already optimized for PHP, consider Hostinger. Their Laravel‑ready stacks include built‑in Redis, PHP‑FPM tuning, and one‑click SSL—perfect for developers who want to skip the grunt work.

No comments:

Post a Comment