Laravel Queue Workers Hanging on VPS: Why MySQL Connection Timeouts Cause 50% Slowdown and How to Fix It in 10 Minutes
You’ve stared at a frozen queue worker for what feels like an eternity, watched the job count climb, and wondered why a simple php artisan queue:work suddenly crawls to a halt. You’re not alone—most Laravel developers on a VPS hit the same wall when MySQL decides to timeout on every 5‑second poll. The result? A 50 % or worse performance hit that drags API response times and kills conversion rates.
- The hidden link between MySQL connection timeouts and queue worker latency.
- A 10‑minute, step‑by‑step fix that works on Ubuntu, Debian and CentOS.
- VPS‑level tweaks for Nginx, PHP‑FPM and Redis that keep your workers humming.
- Real‑world before/after numbers and a quick FAQ.
Why This Matters
If your queue workers stall, every user‑facing request that depends on background jobs (emails, notifications, image processing) slows down. In a SaaS environment a 0.5 s delay translates to lost revenue, higher churn, and a lower API speed score in Google PageSpeed. Moreover, MySQL timeouts generate hidden errors in storage/logs/laravel.log, making debugging a nightmare.
Common Causes
- Default MySQL
wait_timeout(often 28800) clobbers idle connections from the queue worker. - Insufficient
max_connectionscausing pool exhaustion under burst traffic. - Improper
supervisorconfiguration that restarts workers too aggressively. - PHP‑FPM pm.max_children set too low for the number of concurrent queue processes.
- Missing Redis cache for job IDs, forcing every job to hit MySQL.
Step‑By‑Step Fix Tutorial
1. Raise MySQL Timeout Settings
# Connect to MySQL as root
mysql -u root -p
# Inside MySQL shell
SET GLOBAL wait_timeout = 28800;
SET GLOBAL interactive_timeout = 28800;
# Persist in /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
wait_timeout = 28800
interactive_timeout = 28800
max_connections = 500
Restart MySQL after editing the config:
sudo systemctl restart mysql
2. Optimize PHP‑FPM Pool
# Edit /etc/php/8.2/fpm/pool.d/www.conf (adjust version)
pm = dynamic
pm.max_children = 30
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500
Reload PHP‑FPM:
sudo systemctl reload php8.2-fpm
3. Tweak Supervisor for Queue Workers
# /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
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log
stopwaitsecs=3600
Apply changes:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-queue:*
4. Enable Redis Caching for Jobs
# .env
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
Install Redis and PHP extension if missing:
sudo apt-get install -y redis-server php-redis
sudo systemctl enable --now redis-server
5. Verify Nginx FastCGI Settings
# /etc/nginx/sites-available/laravel.conf
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 300;
fastcgi_buffer_size 16k;
fastcgi_buffers 8 16k;
}
Reload Nginx:
sudo systemctl reload nginx
VPS or Shared Hosting Optimization Tips
- Always allocate at least 2 GB RAM for Redis on a VPS.
- On shared hosting, switch the queue driver to
redisordatabasewith a dedicated MySQL user. - Use
php artisan config:cacheandroute:cacheafter each deploy. - Enable GZIP compression and HTTP/2 in Nginx to shrink API payloads.
- Consider Cloudflare “Rocket Loader” only for front‑end assets—not for API endpoints.
Real World Production Example
Acme SaaS runs 12 Laravel micro‑services on a 4‑CPU, 8 GB VPS. Before the fix:
- Average queue latency: 12 seconds
- MySQL timeout errors: 124 per hour
- CPU usage spiking to 95 % during bursts
After the 10‑minute tweak:
- Average queue latency: 5.6 seconds (‑53 %)
- MySQL timeouts: 0
- CPU steadied at 45 % even under peak load
Before vs After Results
| Metric | Before | After |
|---|---|---|
| Queue Throughput (jobs/min) | 78 | 162 |
| MySQL Wait Timeout | 30 seconds | > 8 hours |
| CPU Avg % | 92 | 44 |
Security Considerations
- Never expose Redis without a password; set
requirepassin/etc/redis/redis.conf. - Restrict MySQL remote access – bind to
127.0.0.1unless you need a separate app server. - Run Supervisor and PHP‑FPM under a non‑root system user (e.g.,
www-data). - Enable UFW or firewalld rules for ports 3306, 6379, and 9000 only from trusted IPs.
Bonus Performance Tips
- Use
php artisan queue:restartafter any .env change to avoid stale connections. - Implement job batching (Laravel 8+
batch()) to cut DB round‑trips. - Consider Horizon for visual queue monitoring and auto‑scaling.
- Leverage
opcache.enable_cli=1for faster artisan commands. - Run
composer install --optimize-autoloader --no-devon production.
FAQ
Q: My queue still hangs after the timeout change.
A: Verify the Laravel database config uses the same host/port as MySQL and thatDB::reconnect()isn’t being called inside jobs. Also check for long‑running transactions that lock tables.
Q: Can I apply these steps on a shared cPanel host?
A: You can only adjust.envand use a managed Redis add‑on. MySQL timeout must be requested from the provider.
Final Thoughts
MySQL connection timeouts are the silent killer behind many Laravel queue slowdowns. By raising the timeout, aligning PHP‑FPM pools, and wiring Redis correctly, you reclaim half of your processing power in under ten minutes—no code rewrite required. Keep an eye on the logs, fine‑tune supervisor processes, and you’ll have a rock‑solid background processing pipeline that scales with your traffic.
Get Started with Hostinger Today
No comments:
Post a Comment