Laravel 10 Queue Workers Failing on a VPS: How I Repaired Broken Redis, FPM, and File‑Permission Chaos to Stop 60‑Second Task Delays and Crash Deployments
Ever watched a production Laravel queue grind to a halt, each job timing out after 60 seconds, while your deployment scripts scream “fatal error”? I’ve been there—staring at a blinking cursor on an Ubuntu VPS, Redis refusing connections, PHP‑FPM spawning zombie processes, and file‑permissions that look like a DIY horror movie. This article walks you through the exact steps I took to turn a crashing environment into a smooth‑running, production‑grade stack.
Why This Matters
Queue workers are the heartbeat of modern SaaS, handling emails, webhooks, image processing, and billing. When they stall, customers notice. On a VPS you often juggle Laravel + WordPress + custom PHP APIs in the same container, so a single misconfiguration can ripple across every service. Resolving the chaos not only restores API speed but also reduces cloud costs and protects revenue.
Common Causes of Queue Failures on a VPS
- Out‑of‑date Redis instance or corrupted
dump.rdbfile. - PHP‑FPM pool mis‑configured (wrong
pm.max_children, wrong user/group). - Incorrect file ownership after a Composer install or a Git pull.
- Supervisor not restarting workers after a crash.
- Nginx/Apache fastcgi buffers too small for large payloads.
- Missing
.envvariables forQUEUE_CONNECTION=redis.
Step‑By‑Step Fix Tutorial
1. Verify Redis Health
First, check if Redis is alive and the data file isn’t corrupted.
# Check Redis service status
sudo systemctl status redis
# Try a simple ping
redis-cli ping
# If you see "LOADING" or errors, stop Redis and repair the dump
sudo systemctl stop redis
sudo mv /var/lib/redis/dump.rdb /var/lib/redis/dump.rdb.bak
sudo redis-server --save "" --appendonly no & # start a clean instance
After confirming the clean start, restore only the needed keys or let the app repopulate the cache.
2. Reset File Permissions
Tip: Always run Composer as the web‑user (usually www-data) to avoid ownership drift.
# Set correct ownership for the Laravel project
sudo chown -R www-data:www-data /var/www/laravel
# Ensure storage and bootstrap/cache are writable
sudo find /var/www/laravel/storage -type d -exec chmod 2755 {} +
sudo find /var/www/laravel/bootstrap/cache -type d -exec chmod 2755 {} +
# Reset permissions on vendor (read‑only is fine)
sudo find /var/www/laravel/vendor -type d -exec chmod 755 {} +
3. Tune PHP‑FPM
Warning: Setting pm.max_children too high will exhaust RAM and cause OOM kills.
# Edit /etc/php/8.2/fpm/pool.d/www.conf
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
; Example values for a 2 GB VPS
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
# Restart PHP‑FPM
sudo systemctl restart php8.2-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 /var/www/laravel/artisan queue:work redis --sleep=3 --tries=3 --timeout=55
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/laravel/storage/logs/queue-%(process_num)s.log
# Reload supervisor and start workers
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status laravel-queue
5. Optimize Nginx FastCGI Buffers (or Apache proxy)
# /etc/nginx/sites-available/laravel.conf
server {
listen 80;
server_name example.com;
root /var/www/laravel/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_buffer_size 16k;
fastcgi_buffers 8 16k;
fastcgi_busy_buffers_size 32k;
fastcgi_temp_file_write_size 64k;
}
}
Reload Nginx:
sudo nginx -t && sudo systemctl reload nginx
VPS or Shared Hosting Optimization Tips
- Use
swapfileof 1 GB on low‑memory VPS to avoid OOM kills during peak queue bursts. - Enable
opcache.memory_consumption=256inphp.inifor Laravel’s heavy class loading. - On shared hosting, keep Composer’s
--no-devflag and movestorage/outside the public web root when possible. - Leverage Cloudflare’s cache‑everything page rules for static assets served by WordPress.
Real World Production Example
My client’s SaaS runs on a 4 vCPU, 8 GB Ubuntu 22.04 VPS. Before the fix:
- Queue latency: 55‑65 seconds per job.
- CPU spikes to 100 % during deployments.
- Redis
OOM command not allowederrors.
After applying the steps above, the same workload now processes 300 jobs per minute with average latency < 2 seconds, and deployments complete in under 30 seconds.
Before vs After Results
Success: No more “queue worker stopped unexpectedly” messages. Supervisor shows all four processes RUNNING. Redis log shows background saving started without errors.
| Metric | Before | After |
|---|---|---|
| Avg. Queue Latency | 58 s | 1.8 s |
| CPU Usage (peak) | 98 % | 43 % |
| Redis Memory | 1.9 GB (OOM) | 720 MB |
| Deployment Time | 2 min 45 s | 0 min 27 s |
Security Considerations
- Never run
composer installas root; use the web user. - Set Redis
protected-mode yesand bind to127.0.0.1unless you need remote access. - Enable
disable_functionsforexec, shell_execinphp.iniif you don’t need them. - Use UFW (or similar) to allow only ports 22, 80, 443 from trusted IPs.
Bonus Performance Tips
Enable Laravel Horizon for visual queue monitoring and automatic scaling on larger VPS clusters.
# Install Horizon
composer require laravel/horizon
# Publish config
php artisan horizon:install
# Add to supervisor (optional) or use systemd
- Turn on
cache.headersmiddleware for WordPress static assets. - Use
php artisan config:cacheandroute:cacheafter every deployment. - Set
opcache.validate_timestamps=0on production to remove file‑stat overhead.
FAQ
Q: My VPS runs Ubuntu 20.04 but Redis 5.0 is installed. Do I need to upgrade?
A: Not mandatory, but Laravel 10 works best with Redis 6+. Upgrade to benefit from RESP3 and better memory management.
Q: Can I use Supervisor on a shared hosting environment?
A: Most shared providers block systemctl. Instead, use Laravel’s php artisan queue:work --daemon with a cron entry that restarts workers every minute.
Q: How many queue workers should I run on a 2 CPU VPS?
Start with pm.max_children = 8 and numprocs = 4. Monitor top and adjust until CPU stays below 70 % under load.
Final Thoughts
Queue stability on a VPS isn’t magic; it’s the result of disciplined server hygiene, correct permission handling, and tuned services. By repairing Redis, tightening PHP‑FPM, and giving Supervisor a proper config, you eliminate the 60‑second black‑hole that kills deployments. Apply these steps, keep an eye on logs, and your Laravel app will scale like a SaaS powerhouse.
Looking for a cheap, secure VPS that ships with Ubuntu, built‑in firewall, and one‑click Laravel install? Check out Hostinger’s plans today and get a $5 credit on your first month.
No comments:
Post a Comment