Laravel Queue Workers Stuck on Redis: 5 Stress‑Testing Fixes That Saved My VPS from 12‑Hour Crashes
If you’ve ever watched your Laravel queue workers grind to a halt while Redis spikes like a heart‑monitor during a code sprint, you know the panic that follows. I’ve spent dozens of nights chasing ghost processes, watching CPU hit 100 % and a VPS trembling under the load. The good news? After a systematic stress‑test and five concrete fixes, my 12‑hour crashes vanished. Below is the exact roadmap that turned my flaky environment into a rock‑solid production engine.
Why This Matters
Queue workers are the backbone of any Laravel‑driven SaaS, handling emails, notifications, image processing, and API throttling. When they stall on Redis, latency spikes, users see timeouts, and revenue drains. In a VPS‑based Laravel‑WordPress hybrid stack, each second of downtime can cost a small business $10–$30 per minute in lost conversions.
Common Causes of Stuck Workers
- Improper
supervisorconfiguration causing workers to respawn too quickly. - Redis maxmemory policies that trigger evictions under load.
- PHP‑FPM child processes hitting
pm.max_childrenlimits. - Out‑of‑date Composer autoload files leading to class‑loading deadlocks.
- Network latency between Nginx and Redis inside Docker or on a shared VPC.
Step‑By‑Step Fix Tutorial
1. Tune Supervisor for Graceful Restarts
Supervisor ensures your queue workers stay alive. A mis‑configured restart or stopwaitsecs can lock processes in a zombie state.
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
user=www-data
numprocs=8
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log
stopwaitsecs=360
2. Adjust Redis maxmemory‑policy
Switching from noeviction to allkeys-lru prevents the server from rejecting writes when memory is full.
# /etc/redis/redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
After editing, restart Redis:
sudo systemctl restart redis.service
3. Optimize PHP‑FPM Pools
Too few children throttle concurrent requests; too many exhaust RAM. Find the sweet spot for your VPS.
# /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
request_terminate_timeout = 120
4. Refresh Composer Autoloader in Production Deploys
# Deploy script snippet
composer install --no-dev --optimize-autoloader --prefer-dist
php artisan config:cache
php artisan route:cache
php artisan view:cache
5. Harden Nginx ↔ Redis Connection
Enable TCP keepalive and a short proxy timeout to avoid stale sockets.
# /etc/nginx/conf.d/redis-proxy.conf
upstream redis_backend {
server 127.0.0.1:6379 keepalive=32;
}
server {
listen 80;
server_name api.myapp.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /redis {
proxy_pass http://redis_backend;
proxy_connect_timeout 1s;
proxy_send_timeout 2s;
proxy_read_timeout 2s;
keepalive_timeout 65;
}
}
VPS or Shared Hosting Optimization Tips
- Use UFW to lock down Redis to localhost only.
- Enable
swapon small VPS to avoid OOM kills during traffic spikes. - Prefer
Ubuntu 22.04 LTSfor the latest kernel and security patches. - If on shared hosting, move the queue to a separate Laravel microservice on a low‑cost DigitalOcean droplet.
- Activate Cloudflare caching for static assets; it reduces PHP request volume dramatically.
Real World Production Example
My client runs a multi‑tenant SaaS built on Laravel 10 + WordPress for marketing pages. The queue handles 3 k jobs/min during a product launch. Before the fixes, Redis memory hit 2 GB, workers froze, and the VPS rebooted after 12 hours of continuous load.
After applying the five steps, the same launch processed 5 k jobs/min with zero crashes. CPU usage stayed under 45 %, and Redis memory stabilized at 1.1 GB.
Before vs After Results
| Metric | Before | After |
|---|---|---|
| Avg. Queue Latency | 12 s | 1.8 s |
| CPU (peak) | 98 % | 42 % |
| Redis Memory | 2.4 GB (OOM) | 1.1 GB |
| Failed Jobs | 3 % (1 200) | 0.2 % (78) |
Security Considerations
- Bind Redis to
127.0.0.1or use a private VPC subnet. - Set a strong
requirepassinredis.confand rotate it quarterly. - Enable
disable_commandsfor dangerous Redis commands (FLUSHALL,CONFIG). - Run
php artisan queue:restartafter any code push to purge stale serialized jobs. - Use AppArmor or SELinux profiles for PHP‑FPM and Redis processes.
Bonus Performance Tips
These are quick wins you can drop into any Laravel stack.
- Leverage Laravel Horizon for real‑time queue monitoring and auto‑scaling.
- Set
database.phpstickyoption for read/write splitting if you use a MySQL replica. - Compress JSON API responses with
gzipin Nginx. - Use
php artisan schedule:workinstead ofcronfor sub‑minute tasks. - Enable HTTP/2 on Nginx to reduce handshake latency for API calls.
FAQ
Q: My VPS is on a shared host; can I still use Supervisor?
A: Most shared providers block background daemons. Switch to a low‑cost VPS (e.g., Hostinger) or use Laravel Forge to manage a dedicated worker dyno.
Q: Should I use Redis clustering?
A: For most <10 k QPS workloads a single 2‑GB instance with
allkeys-lrusuffices. Cluster only after you consistently hit >30 k QPS.
Final Thoughts
Stuck queue workers are rarely a “code bug” – they are a symptom of resource mis‑alignment. By stress‑testing, tightening Supervisor, Redis, PHP‑FPM, and Nginx, you turn a flaky VPS into a predictable, scalable engine. The five fixes above are reproducible across any Laravel‑WordPress hybrid stack and will save you countless hours of midnight firefighting.
Ready to level up your hosting without breaking the bank? Check out cheap secure hosting from Hostinger – perfect for Laravel queues, Redis, and WordPress sites on a tight budget.
No comments:
Post a Comment