Monday, May 11, 2026

Laravel Queue Worker Crashes on VPS: 5 Hidden File‑Permission Pitfalls Causing Stuck Jobs and 0‑Second Uptime in 30 Minutes Fix Guide.

Laravel Queue Worker Crashes on VPS: 5 Hidden File‑Permission Pitfalls Causing Stuck Jobs and 0‑Second Uptime in 30 Minutes Fix Guide

Ever watched a queue worker die silently while your users stare at a blank page? You’re not alone. The frustration of “jobs stuck forever” and “artisan queue:work keeps exiting with code 0” is a pain point for every Laravel engineer running on a VPS. In the next few minutes you’ll discover the five permission traps that sabotage your queues, and a step‑by‑step rescue plan that restores 100 % uptime.

Why This Matters

Queue workers are the heartbeat of modern SaaS, handling email delivery, webhook retries, PDF generation, and more. When a worker crashes:

  • Customer experience drops → revenue loss.
  • Retry back‑off floods MySQL, hurting API speed.
  • Server CPU spikes as supervisor respawns dead processes.

Fixing the underlying permission issue not only stabilises your Laravel app but also reduces the load on PHP‑FPM, Redis, and MySQL—key metrics that SEO tools like Google PageSpeed love.

Common Causes of Queue Worker Crashes

  1. Incorrect ownership of storage and bootstrap/cache directories.
  2. Missing execute flag on artisan binary.
  3. Supervisor configuration pointing to a non‑executable PHP binary.
  4. Redis persistence files owned by root causing ERR Permission denied.
  5. Composer autoload files with restrictive chmod set during CI/CD.
INFO: On Ubuntu 22.04 LTS with Nginx + PHP‑FPM, the default www-data user must own all runtime paths. Anything else will silently kill queue:work after the first job finishes.

Step‑By‑Step Fix Tutorial

1️⃣ Verify Current Permissions

# Check ownership
ls -ld storage bootstrap/cache

# Sample output should be:
drwxr-xr-x 5 www-data www-data 4096 Sep 12 12:34 storage
drwxr-xr-x 5 www-data www-data 4096 Sep 12 12:34 bootstrap/cache

2️⃣ Correct Ownership Across the Project

# Replace /var/www/laravel with your app root
sudo chown -R www-data:www-data /var/www/laravel
sudo find /var/www/laravel -type d -exec chmod 2755 {} \;
sudo find /var/www/laravel -type f -exec chmod 0644 {} \;
TIP: Adding the set‑gid bit (2755) forces newly created files inside storage to inherit the www-data group automatically.

3️⃣ Make artisan Executable

sudo chmod +x /var/www/laravel/artisan

4️⃣ Adjust Supervisor Configuration

[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/laravel/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
user=www-data
numprocs=3
redirect_stderr=true
stdout_logfile=/var/www/laravel/storage/logs/worker.log
WARNING: Do NOT run the worker as root. It will bypass permission checks and later fail when writing to storage/framework/cache.

5️⃣ Fix Redis Persistence Permissions

# Typical Redis path on Ubuntu
sudo chown redis:redis /var/lib/redis/dump.rdb
sudo chmod 660 /var/lib/redis/dump.rdb

6️⃣ Restart Services

sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart laravel-queue:*
SUCCESS: All workers now stay alive, logs show “Processing job …” without premature exits.

VPS or Shared Hosting Optimization Tips

  • Swap Management: Allocate at least 1 GB swap on low‑memory VPS to avoid OOM kills.
  • PHP‑FPM Pools: Create a dedicated pool named laravel with pm.max_children=30 for high‑throughput queues.
  • OPcache Settings: opcache.memory_consumption=256 and opcache.validate_timestamps=0 in php.ini boost API speed.
  • MySQL Tuning: Set innodb_buffer_pool_size=70% of RAM and enable slow_query_log for queue‑related queries.
  • Cloudflare Cache‑Purge Hook: Use Laravel webhook to purge CDN after jobs finish (e.g., image optimization).

Real World Production Example

Acme SaaS runs 12 k queued emails per hour on a 2‑vCPU Ubuntu 20.04 VPS. After applying the permission fix, the queue:work logs stopped restarting, CPU dropped from 95 % to 32 %, and email delivery latency fell from 45 s to 7 s.

Before vs After Results

MetricBeforeAfter
CPU Avg95 %32 %
Queue Restarts12/hr0
Job Latency45 s7 s

Security Considerations

  • Never give www-data write access outside storage and bootstrap/cache.
  • Enable open_basedir to restrict PHP file system access.
  • Use Laravel’s queue:restart command instead of killing processes manually.
  • Keep Composer dependencies up‑to‑date: composer audit and composer update --with-all-dependencies.

Bonus Performance Tips

  1. Switch Redis to appendonly no in production for faster writes.
  2. Drop --sleep to 1 and use --daemon flag on high‑rate queues.
  3. Leverage Laravel Horizon for real‑time queue monitoring and auto‑scaling.
  4. Compress large JSON payloads with gzencode before pushing to the queue.

FAQ

Q: My supervisor still reports “EXITED (0)” after the fix.
A: Verify the user directive matches the owner of storage. Restart supervisor and check worker.log for permission errors.
Q: Can I run the queue on a shared hosting plan?
A: Yes, but you must use php artisan queue:work --stop-when-empty via cron every minute, and ensure the host allows exec for the php binary.

Final Thoughts

File‑permission mishaps are the silent killers of Laravel queue reliability. By normalising ownership, tightening supervisor configs, and giving Redis the proper rights, you eliminate the “0‑second uptime” syndrome in under half an hour. The payoff? Faster APIs, happier customers, and a cleaner server‑side stack that scales without screaming for more RAM.

Ready to future‑proof your Laravel deployment? Pair this fix with a low‑cost, high‑performance VPS from Hostinger and you’ll have the resources you need without breaking the bank.

No comments:

Post a Comment