Laravel Queue Workers Stuck on VPS: 10 Proven Fixes for Crashing Jobs, Zero Downtime, and Performance Boost
If you’ve ever watched a Laravel queue grind to a halt on an Ubuntu VPS, felt the heat of angry tickets, and wondered why your jobs keep crashing, you’re not alone. The frustration of a stuck php artisan queue:work process can feel like a silent killer for any SaaS or WordPress‑backed API. In this guide I’ll walk you through ten battle‑tested fixes that get your workers running again, keep your API fast, and let you scale without a single second of downtime.
Why This Matters
Queue workers are the backbone of email dispatch, webhook processing, image manipulation, and even WordPress cron replacements. When they stall, your users notice delayed emails, failed payments, and broken integrations. On a VPS with limited RAM, a single mis‑configured PHP‑FPM pool can bring the whole app to its knees, costing you time and money.
Common Causes of Stuck Laravel Queues
- Exhausted PHP‑FPM workers – processes hitting
max_childrenlimit. - Improper Redis connection timeout causing jobs to time‑out silently.
- Out‑of‑memory (OOM) kills by the kernel on low‑end VPS instances.
- Stale Supervisor config that never restarts failed workers.
- Composer autoload mismatches after a deployment.
- MySQL lock contention on large batch jobs.
- Missing queue:restart after code updates.
- Conflicting Nginx/Apache rewrites that block the
/artisanroute. - Docker container limits that cap CPU usage.
- Improper Cloudflare caching that returns a 502 for queue webhook calls.
Step‑by‑Step Fix Tutorial
1. Audit PHP‑FPM Settings
Check /etc/php/8.2/fpm/pool.d/www.conf (adjust version as needed). Increase pm.max_children and set pm.max_requests to a sane number.
# /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 30 ; default 5 – raise for queue‑heavy apps
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 5000 ; recycle workers to free memory
2. Tune Supervisor for Auto‑Restart
Supervisor will keep your workers alive even after a crash.
# /etc/supervisor/conf.d/laravel-queue.conf
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log
stopwaitsecs=360
After editing, run:
sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl restart laravel-queue:*
3. Optimize Redis Connection
Set a higher timeout and enable persistent connections.
// config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
'read_timeout' => 10,
'persistent' => true,
],
],
4. Prevent OOM Kills
If the kernel kills workers, they will disappear without a log entry.
# /etc/sysctl.conf
vm.overcommit_memory = 1
# Give each worker 256M memory limit
ulimit -v 262144
Apply with sudo sysctl -p.
5. Clean Composer Autoload After Deploy
composer install --no-dev --optimize-autoloader
php artisan event:cache
php artisan view:cache
php artisan route:cache
6. MySQL InnoDB Buffer Pool Tuning
Allocate 70‑80% of RAM to InnoDB on a dedicated VPS.
# /etc/mysql/mysql.conf.d/mysqld.cnf
innodb_buffer_pool_size = 2G ; adjust to 70% of total RAM
innodb_log_file_size = 512M
innodb_flush_method = O_DIRECT
7. Nginx FastCGI Cache for API Endpoints
# /etc/nginx/sites-available/laravel.conf
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_read_timeout 120;
}
8. Enable Laravel Horizon (Optional)
Horizon gives you a beautiful dashboard and automatic scaling.
# Install Horizon
composer require laravel/horizon
# Publish config
php artisan horizon:install
# Start Horizon via Supervisor
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/laravel/horizon.log
9. Cloudflare “Cache Everything” Bypass for Queue URLs
Add a page rule to Cache Level: Bypass for /api/webhook/* endpoints.
10. Docker Resource Limits (If Using Containers)
# docker-compose.yml
services:
app:
image: php:8.2-fpm
deploy:
resources:
limits:
memory: 1g
cpus: '0.75'
VPS or Shared Hosting Optimization Tips
- Choose a VPS with at least 2 vCPU and 4 GB RAM for medium‑size Laravel apps.
- If on shared hosting, restrict queue workers to
--tries=1and use a singlequeue:workprocess via cron. - Enable OPcache in
php.inifor faster script execution. - Turn on Gzip compression in Nginx/Apache to reduce bandwidth for API responses.
- Use UFW firewall to only allow Redis (port 6379) from localhost.
Real World Production Example
Acme SaaS migrated from a 1 vCPU 2 GB Ubuntu 22.04 VPS to a 4 vCPU 8 GB DigitalOcean droplet. After applying the ten fixes, queue latency dropped from 45 seconds to under 2 seconds, and crash reports vanished.
Before vs After Results
| Metric | Before | After |
|---|---|---|
| Avg. Job Time | 45 s | 1.8 s |
| Failed Jobs / day | 23 | 0 |
| CPU Utilization | 95 % | 45 % |
| Memory Pressure | High (OOM) | Stable |
Security Considerations
- Never expose Redis to the public internet – bind to
127.0.0.1only. - Rotate queue passwords in
.envquarterly. - Use Supervisor user
www-dataand setchmod 750onstoragedirectories. - Enable AppArmor or SELinux profiles for PHP‑FPM processes.
- Run
php artisan queue:clearafter a security patch to flush potentially compromised jobs.
Bonus Performance Tips
- Switch to Laravel Octane with Swoole for ultra‑fast queue handling.
- Batch database writes inside jobs using
DB::transaction()to reduce lock time. - Enable Redis pipelining for bulk job pushes.
- Use Job Middleware to throttle expensive tasks.
- Schedule a nightly
php artisan queue:restartvia cron to clear stale caches.
FAQ
Q: My queue keeps restarting after a code deploy. What should I do?
A: Run
php artisan queue:restartas part of your deployment script. Supervisor will pick up the new processes automatically.
Q: Can I run Laravel queues on a shared WordPress host?
A: Yes, but limit workers to a single process, use the
databasedriver, and triggerphp artisan schedule:runvia a cron every minute.
Final Thoughts
Stuck Laravel queue workers are rarely a mystical bug; they are usually the result of resource limits, mis‑configured services, or forgotten deployment steps. By methodically applying the ten fixes above—tuning PHP‑FPM, supervising workers, optimizing Redis and MySQL, and hardening the VPS—you’ll achieve zero‑downtime deployments, dramatically faster API responses, and a queue that actually works for your users.
Ready to level up your hosting? Cheap, secure VPS hosting from Hostinger gives you the RAM and CPU headroom you need for all the fixes in this guide—plus a 30‑day money‑back guarantee.
No comments:
Post a Comment