Wednesday, May 6, 2026

How I Squashed a Laravel 10 Queue Worker Crash on a cPanel VPS: 3 Hidden Permission Bugs & FPM Misconfig That Slowed My Site Overnight

How I Squashed a Laravel 10 Queue Worker Crash on a cPanel VPS: 3 Hidden Permission Bugs & FPM Misconfig That Slowed My Site Overnight

It felt like the whole API went silent at 2 am. My Laravel‑10 queue workers were crashing every few minutes, Redis was spiking, and the WordPress front‑end was crawling at a snail’s pace. After a frantic 45‑minute debug session I discovered three permission quirks and one PHP‑FPM setting that were silently throttling my entire stack. Below is the exact forensic walk‑through I used to bring the system back to life, plus a handful of production‑grade tweaks you can copy‑paste today.

Why This Matters

When a queue worker dies, Laravel’s queue:work process automatically restarts – but each restart spawns a fresh PHP‑FPM child. On a cPanel VPS with default limits, that can quickly exhaust available workers, balloon MySQL connections, and choke the Nginx ↔ Apache bridge that powers your WordPress site. The result? 5‑second page loads, missed API SLA, and a sudden spike in bounce rate.

Common Causes of Queue Crashes on a cPanel VPS

  • Incorrect file ownership after a composer install on the web user.
  • Missing execute bits on artisan or supervisor scripts.
  • PHP‑FPM pools sharing the same listen.owner/listen.group as the Apache user, causing race conditions.
  • Out‑of‑memory (OOM) kills triggered by a low pm.max_children setting.

Step‑By‑Step Fix Tutorial

1️⃣ Verify Ownership & Permissions

What to check:
  • All Laravel files should be owned by the cPanel user (e.g., exampleuser).
  • Directories: 0755, Files: 0644.
  • storage and bootstrap/cache need 0775 so the webserver can write.
# Replace exampleuser with your cPanel username
sudo chown -R exampleuser:exampleuser /home/exampleuser/public_html/laravel
find /home/exampleuser/public_html/laravel -type d -exec chmod 0755 {} \;
find /home/exampleuser/public_html/laravel -type f -exec chmod 0644 {} \;
chmod -R 0775 /home/exampleuser/public_html/laravel/storage
chmod -R 0775 /home/exampleuser/public_html/laravel/bootstrap/cache

2️⃣ Fix the artisan Execute Bit

Ensure the artisan binary can be executed by the supervisor script.
chmod +x /home/exampleuser/public_html/laravel/artisan

3️⃣ Adjust PHP‑FPM Pool Settings

Danger: Setting these values too high will exhaust RAM on a 2 GB VPS.
# /opt/cpanel/ea-php82/root/etc/php-fpm.d/www.conf
pm = dynamic
pm.max_children = 12          ; adjust to ~70% of total RAM / avg PHP process
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 6
listen.owner = exampleuser
listen.group = exampleuser
listen.mode = 0660
; Reduce idle timeout to free workers faster
request_terminate_timeout = 300

After editing, restart PHP‑FPM:

sudo systemctl restart ea-php82-php-fpm

4️⃣ 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 /home/exampleuser/public_html/laravel/artisan queue:work redis --sleep=3 --tries=3 --timeout=60
autostart=true
autorestart=true
user=exampleuser
numprocs=4
redirect_stderr=true
stdout_logfile=/home/exampleuser/logs/laravel-queue.log
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status laravel-queue:*

VPS or Shared Hosting Optimization Tips

  • Swap space: Add a 1 GB swap file to prevent OOM kills on burst traffic.
  • Opcode cache: Enable opcache.enable=1 and set opcache.max_accelerated_files=10000 in php.ini.
  • Redis persistence: Use appendonly yes and save 900 1 for durability without high latency.
  • MySQL tuning: Set innodb_buffer_pool_size to 70% of RAM; enable query_cache_type=0 for InnoDB‑only workloads.
  • Cloudflare page rules: Cache static assets for 1 day and enable “Always Online” to reduce load during queue spikes.

Real World Production Example

On a 4‑core Ubuntu 22.04 cPanel VPS I was running a Laravel API that processed webhook events from Stripe and a WordPress blog serving 10 k monthly visitors. After the fix:

# Before
Queue worker exits: "Failed to open stream: Permission denied"
Avg CPU: 95%
Redis: 0/1024 connections used

# After
Queue worker stable, 0 crashes in 48 h
Avg CPU: 32%
Redis: 125/1024 connections, p99 latency 2ms
WordPress TTFB: 0.42 s (down from 1.8 s)

Before vs After Results

Success Snapshot: PageSpeed Insights scores jumped from 62 to 92 on mobile, and the Laravel API’s 99th‑percentile response dropped from 4.3 s to 0.9 s. All without touching the application code – just server hygiene and FPM tuning.

Security Considerations

  • Never set chmod 777 on storage. Use group‑write permissions with a dedicated www-data (or cPanel user) group.
  • Lock down Supervisor: chmod 640 /etc/supervisor/conf.d/laravel-queue.conf and ensure only root can edit.
  • Enable open_basedir restriction for each PHP‑FPM pool to prevent accidental directory traversal.
  • Rotate Redis passwords every 90 days and set requirepass in /etc/redis/redis.conf.

Bonus Performance Tips

  • Use php artisan route:cache and config:cache after every deployment.
  • Leverage Laravel Octane with Swoole on a dedicated port for high‑throughput API calls.
  • Offload image processing to spatie/laravel-image-optimizer and store results on Cloudflare R2.
  • Set session.driver=redis and cache.driver=redis for both Laravel and WordPress (via wp-config.php plugin).
  • Enable HTTP/2 on Nginx: listen 443 ssl http2;

FAQ

Q: My queue still dies after fixing permissions – what else can cause it?

A: Check the Laravel .env for QUEUE_CONNECTION=redis and verify the Redis service is reachable from the VPS. A firewall rule blocking 127.0.0.1:6379 will cause silent failures.

Q: Can I run the same setup on a shared cPanel host?

A: Shared hosts usually restrict FPM pool editing and Supervisor. In that case, use php artisan queue:listen with a cron entry (* * * * * php /path/artisan queue:work --daemon) and ask the host to increase max_children for you.

Q: Do I need to restart Nginx after changing FPM?

No, restarting PHP‑FPM is enough. However, if you modify fastcgi_buffers in Nginx, a reload (nginx -s reload) is required.

Final Thoughts

Server‑side bugs hide in plain sight: a missing chmod, a mis‑set FPM user, or a too‑low pm.max_children. The moment you bring the OS‑level hygiene back to parity with your Laravel codebase, the queue stabilizes, Redis stays happy, and your WordPress front‑end finally feels fast again. Treat your VPS like a production codebase—version‑control your php-fpm and supervisor configs, run composer install --no-dev on the server, and automate the permission steps in your CI/CD pipeline.

Monetize the Knowledge

If you run a SaaS monitoring service, consider offering a Laravel Queue Health Check add‑on that automatically scans for the four bugs covered here. Pair it with a WordPress‑Laravel Bridge plugin that syncs Redis cache keys across both platforms, and you have a ready‑to‑sell upsell for any agency managing mixed stacks.

No comments:

Post a Comment