Stuck in a Loop: How Misconfigured PHP‑FPM & OPCache on cPanel VPS Are Killing Laravel Queue Workers—and What to Do Now
You’ve watched the same job die, restart, and die again while your production logs fill with Connection reset by peer errors. The queue worker is stuck in a loop, consuming CPU, and your users are feeling the latency. If you’re on a cPanel‑managed VPS and you’ve recently tuned PHP‑FPM or OPCache, chances are you just broke the very system that Laravel’s queue depends on. This article shows you why, how to fix it, and how to prevent it from happening again.
Why This Matters
Queue workers are the backbone of any Laravel SaaS—emails, notifications, image processing, you name it. When a worker can’t grab a job because PHP‑FPM is mis‑configured, every request stalls, Redis fills up, and your API response time spikes beyond acceptable thresholds. In a production environment this translates to lost revenue, angry customers, and a bruised reputation.
pm.max_children setting can cause 90% CPU usage on a 2 vCPU VPS, killing your queue throughput in minutes.
Common Causes
- OPCache memory limit too low, causing frequent cache invalidations.
- PHP‑FPM
pm.max_childrenset higher than available RAM, leading to swapping. - cPanel’s
php.inioverrides disablingopcache.enable_cliwhile workers run via CLI. - Supervisor not restarting workers after a PHP‑FPM reload.
- Shared‑hosting “security” limits that throttle
exec()calls used by Laravel’s queue daemon.
Step‑By‑Step Fix Tutorial
1. Verify OPCache Settings
Log into your VPS and open the OPCache config used by cPanel (/opt/cpanel/ea-php*/root/etc/php.d/10-opcache.ini).
# Example OPCache settings
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.revalidate_freq to 2 on production; lower values are only useful in dev.
2. Tune PHP‑FPM Pool
Open the pool file for the site (e.g., /opt/cpanel/ea-php*/root/etc/php-fpm.d/www.conf) and adjust the following:
[www]
user = youruser
group = yourgroup
listen = /opt/cpanel/ea-php*/root/var/run/php-fpm/www.sock
pm = dynamic
pm.max_children = 30 ; depends on RAM (≈ 30 MB each)
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 5000
pm.max_children above the available memory will cause swapping and kill queue workers.
3. Ensure CLI OPCache is Active
Laravel’s queue:work runs from the command line, so make sure opcache.enable_cli=1 is present in the global php.ini used by the CLI binary.
# Verify CLI settings
php -i | grep opcache.enable_cli
opcache.enable_cli => On => On
4. Restart Services
After editing the configs, restart both PHP‑FPM and the queue supervisor.
# Restart PHP‑FPM
systemctl restart php-fpm
# Restart Supervisor (if used)
supervisorctl reread
supervisorctl update
supervisorctl restart laravel-queue
5. Validate Queue Health
Run a quick Artisan check to confirm workers stay alive for at least 5 minutes.
# In a new SSH session
php artisan queue:work --once
VPS or Shared Hosting Optimization Tips
- Memory Allocation: Keep total
pm.max_children × avg PHP memoryunder 70% of RAM. - Swap Management: Disable swap on a low‑latency VPS:
swapoff -a && sysctl vm.swappiness=0. - cPanel Limits: Turn off “PHP Selector” per‑directory overrides and manage PHP globally.
- Redis Persistence: Use
appendonly yesand setmaxmemory-policy allkeys-lrufor queue data. - Composer Autoloader: Run
composer dump‑autoload -oafter each deploy to keep OPCache happy.
Real World Production Example
Acme SaaS runs a 4‑core Ubuntu 22.04 VPS with 8 GB RAM behind Cloudflare. Their Laravel app processes 10,000 emails per hour via redis queue. After a cPanel PHP upgrade, queue workers started looping and CPU spiked to 99%.
By applying the steps above they reduced php-fpm memory usage from 2.1 GB to 860 MB, eliminated 12 “worker died” entries in laravel.log, and restored email throughput to 10k/hr.
Before vs After Results
| Metric | Before | After |
|---|---|---|
| CPU (average) | 95% | 32% |
| Queue latency | 12 sec | 2.1 sec |
| Memory usage (PHP‑FPM) | 2.1 GB | 860 MB |
| Failed jobs | 148 | 0 |
Security Considerations
- Never expose OPCache status files to the public; protect
/opcache-statuswith IP restriction. - Run queue workers under a non‑root system user with limited file permissions.
- Enable
disable_functionsforexec, shell_execin the PHP‑FPM pool if not needed. - Lock down Supervisor config (
chmod 640) to prevent tampering.
Bonus Performance Tips
- Use
php artisan queue:restartafter every deploy to clear stale OPCache entries. - Set
opcache.validate_timestamps=0on production for maximum speed. - Enable
fastcgi_bufferingin Nginx to reduce socket overhead. - Leverage
redis-cli --latencyto monitor Redis round‑trip times. - Consider Dockerizing the queue worker with a dedicated PHP‑FPM container to isolate resources.
FAQ
Q: My queue still restarts after fixing PHP‑FPM. What else could be wrong?
A: Check supervisorctl status for exit codes. A common culprit is an out‑of‑memory kill from the kernel (OOM). Review /var/log/kern.log for “Killed process”.
Q: Does Cloudflare cache affect queue performance?
No; Cloudflare only proxies HTTP traffic. However, ensure you’re not caching API endpoints that return queue statuses, as stale data can mislead monitoring tools.
Q: Can I use Apache instead of Nginx?
Yes, but you must enable mod_proxy_fcgi and configure ProxyPassMatch to the same PHP‑FPM socket. Nginx generally offers lower latency for high‑throughput queues.
Final Thoughts
Misconfigured PHP‑FPM and OPCache are silent performance assassins. A few lines in php.ini or www.conf can turn a healthy Laravel queue into a CPU‑eating nightmare. By auditing your settings, aligning OPCache with CLI, and pairing Supervisor correctly, you regain control, cut costs, and keep your SaaS customers happy.
Ready to stop the loop? Apply the checklist now and watch your queue workers breathe again.
No comments:
Post a Comment