Sunday, May 10, 2026

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

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.rdb file.
  • 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 .env variables for QUEUE_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 swapfile of 1 GB on low‑memory VPS to avoid OOM kills during peak queue bursts.
  • Enable opcache.memory_consumption=256 in php.ini for Laravel’s heavy class loading.
  • On shared hosting, keep Composer’s --no-dev flag and move storage/ 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 allowed errors.

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.

MetricBeforeAfter
Avg. Queue Latency58 s1.8 s
CPU Usage (peak)98 %43 %
Redis Memory1.9 GB (OOM)720 MB
Deployment Time2 min 45 s0 min 27 s

Security Considerations

  • Never run composer install as root; use the web user.
  • Set Redis protected-mode yes and bind to 127.0.0.1 unless you need remote access.
  • Enable disable_functions for exec, shell_exec in php.ini if 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.headers middleware for WordPress static assets.
  • Use php artisan config:cache and route:cache after every deployment.
  • Set opcache.validate_timestamps=0 on 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