Saturday, May 9, 2026

Laravel Queue Workers Stop on cPanel VPS: How a Broken File Permission Slowed Production, Locked Out of Cron Jobs, and Broke the Whole Site in Minutes

Laravel Queue Workers Stop on cPanel VPS: How a Broken File Permission Slowed Production, Locked Out of Cron Jobs, and Broke the Whole Site in Minutes

If you’ve ever watched a healthy Laravel queue grind to a halt while your customers wait, you know the gut‑punch feeling of a silent production outage. I spent a frantic afternoon on a cPanel VPS watching php artisan queue:work die on a permission error, my cron jobs disappear, and the entire WordPress front‑end return 500 errors. The culprit? A single mis‑set directory permission that cascaded through PHP‑FPM, Supervisor, and even the Cloudflare cache. This post walks you through the exact steps I used to diagnose, fix, and future‑proof a Laravel + WordPress stack on a VPS.

Why This Matters

Queue workers are the heartbeat of any modern PHP SaaS. When they stop:

  • Background jobs like email, notifications, and payments pile up.
  • API response times balloon because tasks are no longer off‑loaded.
  • Customer churn spikes as users encounter broken features.
  • SEO rankings dip when your site returns 5xx errors.

In a production environment hosted on a shared cPanel VPS, a single permission slip can cascade into a full‑site outage, wiping out revenue in minutes.

Common Causes of Queue Failures on cPanel VPS

  • Incorrect storage or bootstrap/cache permissions after a Composer update.
  • Supervisor configuration pointing to a non‑existent user or wrong php binary.
  • cPanel’s php-fpm pool limit hitting the pm.max_children threshold.
  • File ownership conflicts between the Apache/Nginx user (often nobody) and the SSH user.
  • SELinux or ModSecurity rules blocking exec calls.
INFO: Even if you run php artisan queue:restart, a stale PID file with wrong permissions will keep the old worker alive. Always check storage/framework/queues after a deployment.

Step‑by‑Step Fix Tutorial

1. Verify the Permission Error

tail -f storage/logs/laravel.log | grep -i "permission"

If you see Permission denied referencing storage/framework/cache/data, you’ve found the symptom.

2. Reset Ownership & Permissions

# Replace USERNAME with your SSH user
sudo chown -R USERNAME:USERNAME /home/USERNAME/public_html/laravel
sudo find /home/USERNAME/public_html/laravel -type d -exec chmod 755 {} \;
sudo find /home/USERNAME/public_html/laravel -type f -exec chmod 644 {} \;

# Give write access to the storage & bootstrap/cache folders
sudo chmod -R 775 storage bootstrap/cache
TIP: On cPanel, the default user is usually USERNAME. Do NOT use root for Laravel files; it breaks Supervisor.

3. Re‑configure Supervisor

[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /home/USERNAME/public_html/laravel/artisan queue:work --sleep=3 --tries=3 --daemon
autostart=true
autorestart=true
user=USERNAME
numprocs=3
redirect_stderr=true
stdout_logfile=/home/USERNAME/public_html/laravel/storage/logs/worker.log
stopwaitsecs=3600

After editing /etc/supervisord.conf (or /etc/supervisor/conf.d/laravel-queue.conf on cPanel), run:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart laravel-queue:*

4. Fix Cron Job Lockout

cPanel stores cron entries in /var/spool/cron/USERNAME. A permission change can erase them.

# Restore cron from backup if you have one
crontab /home/USERNAME/cron.backup

# If not, re‑add the Laravel schedule
* * * * * /usr/bin/php /home/USERNAME/public_html/laravel/artisan schedule:run >> /dev/null 2>&1

5. Restart PHP‑FPM & Nginx/Apache

# For PHP‑FPM (cPanel uses CloudLinux)
sudo systemctl restart php-fpm

# If you’re on Nginx behind Apache (reverse proxy)
sudo systemctl restart nginx
sudo systemctl restart httpd
SUCCESS: After these steps the queue workers were back online, cron resumed, and the WordPress front‑end returned to 200 OK.

VPS or Shared Hosting Optimization Tips

  • Use a dedicated queue user: Create queueuser with limited privileges and point Supervisor to it.
  • Enable opcache: Add opcache.enable=1 in php.ini for a 15‑30% boost.
  • Configure PHP‑FPM pools per app: Separate Laravel and WordPress pools to avoid resource contention.
  • Set pm.max_children based on RAM: Rough rule – 256 MB per child on an 8 GB VPS.
  • Leverage Redis for queue driver: QUEUE_CONNECTION=redis and set REDIS_HOST=127.0.0.1.
  • Use Cloudflare page rules: Cache static assets, but bypass /api/* and /queue/* paths.

Real World Production Example

Company Acme SaaS ran a Laravel API on a 2 vCPU, 4 GB cPanel VPS with a WordPress blog sharing the same domain. After a nightly Composer install, storage permissions were set to 600. Within 10 minutes the queue stalled, email notifications stopped, and the blog returned 502 errors. The fix above restored service in 18 minutes and prevented future incidents by adding a post‑deploy script:

# post‑deploy.sh
#!/bin/bash
cd /home/USERNAME/public_html/laravel
php artisan down
composer install --no-dev --optimize-autoloader
php artisan migrate --force
chmod -R 775 storage bootstrap/cache
chown -R USERNAME:USERNAME .
php artisan up
supervisorctl restart laravel-queue:*

Before vs After Results

Metric Before Fix After Fix
Queue Throughput 0 jobs/min 1,200 jobs/min
Cron Success Rate 12 % 100 %
Site Uptime 99.2 % 99.99 %
Average API Latency 850 ms 320 ms

Security Considerations

WARNING: Never set 777 on storage or bootstrap/cache. It opens the door for remote code execution if an attacker can upload a PHP file.

Best practices:

  • Use chmod 750 for directories and chmod 640 for files.
  • Enable file_uploads = Off for the queue worker user.
  • Restrict SSH keys to a single IP.
  • Run composer audit after each dependency update.

Bonus Performance Tips

  • Cache heavy queries: Cache::remember('users_active', 300, fn()=>User::where('active',1)->get());
  • Use Horizon: Laravel Horizon gives you real‑time queue metrics and auto‑scales workers.
  • Enable MySQL query cache: query_cache_type = ON and query_cache_size = 64M in my.cnf.
  • Segment Redis keys: Prefix queues with app:queue: to avoid collisions with WordPress sessions.
  • Compress assets with Cloudflare Polish: Automatic WebP and Brotli reduce bandwidth.

FAQ

Q: My queue keeps restarting after I fix permissions. What’s wrong?

A: Check the Supervisor log (worker.log) for “Laravel worker terminated with status 1”. Usually a stale .env cache or missing APP_KEY is the cause.

Q: Can I run Laravel queues on the same cPanel user as WordPress?

A: Technically yes, but separate users prevent a WordPress plugin from breaking the queue’s file permissions.

Q: Does CloudLinux limit the number of processes?

A: Yes, LVE limits can throttle php-fpm children. Increase the LVE limit or move to a plain Ubuntu VPS for heavy workloads.

Q: Should I use Redis or database driver for queues?

A: Redis is far more performant (sub‑millisecond latency) and survives database restarts. Use QUEUE_CONNECTION=redis in production.

Final Thoughts

Permissions may feel like a “small” detail, but on a cPanel VPS they can instantly cripple Laravel queues, cron jobs, and even a WordPress front‑end. By enforcing a strict permission model, automating post‑deploy fixes, and monitoring Supervisor, you can keep your queue humming and your revenue flowing. Remember: a few lines of Bash and a well‑written Supervisor config are worth the cost of a downed site.

TIP: Combine this guide with a monitoring service like cheap secure hosting from Hostinger to get real‑time alerts on queue failures before they affect customers.

© 2026 Your Laravel & WordPress Optimization Blog. All rights reserved.

No comments:

Post a Comment