Saturday, May 9, 2026

Laravel Queue Workers Stuck on Your VPS – Why Jobs Crash Under High FPM Load and How to Fix It Fast in Nginx/Apache Environments, No More 502s, Restart Workers & Redis Dormancy

Laravel Queue Workers Stuck on Your VPS – Why Jobs Crash Under High FPM Load and How to Fix It Fast in Nginx/Apache Environments

Ever watched a Laravel job disappear into the void, only to get a nasty 502 Bad Gateway right after a traffic spike? You’re not alone. Hundreds of devs on the US east coast hit this wall nightly when PHP‑FPM hits its ceiling and the queue workers silently die. In this article we tear apart the problem, show you step‑by‑step how to rescue your workers, and lock down the server so 502s become a thing of the past.

Why This Matters

Queue workers are the backbone of any Laravel‑powered SaaS, WordPress‑integrated API, or high‑traffic e‑commerce site. When they stop:

  • Emails bounce, invoices stall, and user notifications never arrive.
  • Background cleanup jobs (image optimization, cache busting, webhooks) pile up, causing database bloat.
  • CPU spikes spike further, leading to cascading 502/504 errors that hurt SEO and conversion rates.

In short, a stalled worker chain equals lost revenue and angry customers.

Common Causes

  • PHP‑FPM max_children reached – the pool is full, new requests wait, workers time out.
  • Supervisor mis‑configuration – not enough processes, wrong stopwaitsecs, or missing autostart.
  • Redis connection limits – default 10 connections saturate under load.
  • OOM kills – VPS memory under‑provisioned, kernel kills php-fpm or redis.
  • Improper Nginx/Apache fastcgi buffers – large payloads cause 502 before PHP gets a chance.

Step‑By‑Step Fix Tutorial

INFO: The steps assume an Ubuntu 22.04 LTS VPS with Nginx, PHP‑8.2‑FPM, Redis 7, and Supervisor installed via apt. Adjust paths for Debian, CentOS or Docker accordingly.

1. Diagnose the FPM Pool

# Check current FPM status
curl -s http://127.0.0.1/status | grep "pool"
# View active processes
ps -ylC php-fpm8.2 --sort:-%mem | head

2. Tune php‑fpm.conf

# /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 120          ; increase from default 5‑10
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 40
request_terminate_timeout = 300
rlimit_core = 0
rlimit_files = 10240
TIP: Use pm = ondemand only for low‑traffic sites; high‑traffic SaaS benefits from dynamic with a higher max_children.

3. Optimize Nginx FastCGI Buffers

# /etc/nginx/sites-available/laravel.conf
location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_busy_buffers_size 64k;
    fastcgi_connect_timeout 60s;
    fastcgi_read_timeout 300s;
}

4. Re‑configure Supervisor for Laravel Queues

# /etc/supervisor/conf.d/laravel-queue.conf
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/yourapp/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=8
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log
stopwaitsecs=3600
WARNING: Do not set numprocs higher than pm.max_children / 2 or you’ll starve PHP‑FPM.

5. Increase Redis Max Connections

# /etc/redis/redis.conf
maxclients 10000
tcp-backlog 511
stop-writes-on-bgsave-error no

6. Restart Services

sudo systemctl daemon-reload
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx
sudo systemctl restart supervisor
sudo systemctl restart redis-server

7. Verify Health

# Check Supervisor status
supervisorctl status

# Tail queue logs
tail -f /var/log/laravel/queue.log

# Verify FPM children
php-fpm8.2 -t && systemctl status php8.2-fpm

VPS or Shared Hosting Optimization Tips

  • Allocate at least 2 GB RAM for PHP‑FPM + Redis on a 4 GB VPS.
  • Use swap as a safety net, but keep it under 1 GB to avoid heavy I/O.
  • If on shared hosting, request higher max_execution_time and memory_limit from support.
  • Enable Cloudflare “Automatic Platform Optimization” to offload static assets.
  • Consider Docker with php-fpm and redis in separate containers for isolated resource caps.

Real World Production Example

Acme SaaS runs a Laravel 10 API on a 8 vCPU, 16 GB Ubuntu VPS. After a marketing email blast, the job queue stalled, causing a 502 spike that knocked out the dashboard for 12 minutes.

By applying the steps above:

  • Increased pm.max_children from 30 to 120.
  • Set Supervisor numprocs=16 and stopwaitsecs=7200.
  • Adjusted Nginx buffers to handle 2 MB payloads.
  • Boosted Redis maxclients to 5000.

The result was a 0% error rate during another 20k‑request load test.

Before vs After Results

Metric Before After
Avg Queue Latency 12 seconds 1.4 seconds
502 Errors (per hour) 27 0
CPU avg % (peak) 95% 68%

Security Considerations

  • Run PHP‑FPM and Redis under non‑root users (e.g., www-data).
  • Enable request_terminate_timeout to stop runaway scripts.
  • Restrict Redis to localhost or a private VPC subnet.
  • Use disable_functions for exec, system, shell_exec in php.ini.
  • Set open_basedir to the application root to prevent path traversal.

Bonus Performance Tips

SUCCESS: Adding Laravel Horizon gives you a visual dashboard, auto‑scaling, and built‑in Redis connection pooling. Install with composer require laravel/horizon and configure config/horizon.php for balance: 'simple' on modest VPSes.
  • Use php artisan optimize:clear after each deployment.
  • Cache config and routes: php artisan config:cache && php artisan route:cache.
  • Leverage OPCache: opcache.enable=1 and opcache.max_accelerated_files=10000.
  • Compress responses with gzip in Nginx.
  • Enable MySQL slow query log and add indexes on foreign keys used by queued jobs.

FAQ

Q: My queue still dies after the fix, what next?

A: Check kernel OOM logs (dmesg | grep -i kill) and consider upgrading to a larger VPS or moving Redis to a managed instance.

Q: Can I use Apache instead of Nginx?

Yes. Replace the fastcgi block with ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/var/www/yourapp/$1 and tune ProxyTimeout to >300s.

Q: Do I really need Supervisor?

Supervisor provides process monitoring and automatic restarts. For Docker, use docker‑compose restart policies instead.

Q: How many queue workers should I run?

Start with numprocs = (pm.max_children / 2). Adjust based on CPU idle time (use top or htop).

Final Thoughts

Stuck Laravel queue workers are rarely a code bug; they’re a server‑resource symptom. By proactively sizing PHP‑FPM, sharpening Nginx/Apache buffers, and configuring Supervisor and Redis with production‑grade limits, you eliminate the 502 avalanche and keep your SaaS humming.

Take the checklist, apply it on a staging clone, and you’ll see the same zero‑error results as the big players—without buying a costly managed platform.

BONUS: Looking for cheap, secure hosting to spin up a new Laravel‑Vue stack? Check out Hostinger – blazing SSD, one‑click Laravel install, and 24/7 US‑based support.

No comments:

Post a Comment