Friday, May 8, 2026

Laravel Queue Crash on cPanel VPS: Why MySQL Connections Drop and How to Fix It Fast

Laravel Queue Crash on cPanel VPS: Why MySQL Connections Drop and How to Fix It Fast

You’re watching your queue workers explode, the MySQL error log screams “Too many connections”, and the whole Laravel app grinds to a halt. It feels like every time you spin up a new job, the VPS takes a deep breath and chokes. If you’ve ever stared at a blinking cursor wondering whether to rewrite the whole architecture, this guide is for you. We’ll dig into the root cause, patch the crash, and give you a hardened VPS that can handle 10k+ jobs a day without shedding connections.

Why This Matters

Queue workers are the heartbeat of any modern Laravel‑powered SaaS. They power email campaigns, webhook dispatches, image processing, and more. When MySQL connections start dropping, you lose:

  • Real‑time notifications
  • Customer‑facing API reliability
  • Revenue‑critical background billing jobs

In production environments—especially on a cPanel VPS shared with WordPress—those lost jobs become lost money.

Common Causes

  • Unlimited queue workers: Supervisor spawns more processes than MySQL can handle.
  • Default PHP‑FPM pool settings: Each FPM child opens a DB connection that never closes.
  • cPanel’s MySQL limits: Shared hosting caps connections at 150 by default.
  • Missing Redis cache: Jobs hit the DB for every lock check.
  • Improper .env values: DB_MAX_CONNECTIONS not synced with MySQL server.

Step‑By‑Step Fix Tutorial

1. Audit Your Current Connection Count

# Check active connections
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
# Show max allowed connections
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"

2. Tune MySQL for Higher Concurrency

Tip: Increase max_connections only if your VPS has enough RAM. A good rule of thumb is 1 MB per connection.

# Edit /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
max_connections = 500
innodb_buffer_pool_size = 2G   # Adjust to 70% of RAM

After editing, restart MySQL:

systemctl restart mysql

3. Limit Laravel Queue Workers

Keep workers around 2 × CPU cores and enforce a --timeout of 60 seconds.

# /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=60
autostart=true
autorestart=true
numprocs=4               ; 2 × 2‑core VPS
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log

Reload Supervisor:

supervisorctl reread && supervisorctl update

4. Enable Persistent Connections in Laravel

// config/database.php
'mysql' => [
    'driver'         => 'mysql',
    'host'           => env('DB_HOST', '127.0.0.1'),
    'port'           => env('DB_PORT', '3306'),
    'database'       => env('DB_DATABASE', 'forge'),
    'username'       => env('DB_USERNAME', 'forge'),
    'password'       => env('DB_PASSWORD', ''),
    'strict'         => true,
    'options'        => extension_loaded('pdo_mysql') ? [
        PDO::ATTR_PERSISTENT => true,
    ] : [],
],

5. Add Redis for Queue Locking and Caching

# Install Redis (Ubuntu)
apt-get update && apt-get install -y redis-server

# Enable Redis in Laravel
// .env
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

6. Adjust PHP‑FPM Pool Settings

Reduce pm.max_children to avoid spawning more DB connections than MySQL allows.

# /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50      ; depends on RAM
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
php_admin_value[error_log] = /var/log/php-fpm/www-error.log

Restart PHP‑FPM:

systemctl restart php8.2-fpm

VPS or Shared Hosting Optimization Tips

  • Swap Management: Disable swap on production VPS to force proper memory usage.
  • cPanel MySQL Limits: Increase max_user_connections via WHM → “SQL Services → MySQL/MariaDB Configuration”.
  • Apache vs Nginx: Nginx + PHP‑FPM yields lower memory foot‑print. If you must stay on Apache, enable mod_proxy_fcgi and Worker MPM.
  • Composer Autoloader Optimisation: Run composer install --optimize-autoloader --no-dev during deployment.
  • Cloudflare Caching: Off‑load static assets; set Cache‑Level: Aggressive to reduce DB hits.

Real World Production Example

Acme SaaS runs a 8‑core Ubuntu 22.04 VPS with 32 GB RAM, Nginx, PHP‑FPM 8.2, and Redis. Before the fix they hit max_connections = 151, losing 12 % of queued emails during peak traffic.

After applying the steps:

  • MySQL max_connections raised to 500.
  • Supervisor limited workers to 8 processes.
  • Redis reduced DB lock queries by 87 %.
  • PHP‑FPM pool set to 80 children, keeping RAM usage stable.

Result: 0 MySQL connection errors for a month, email deliverability up 15 %, and CPU average dropped from 78 % to 42 %.

Before vs After Results

Metric Before After
MySQL Connections (peak) 160 (exceeded) 312 (within limit)
Queue Workers 12 (over‑provisioned) 8 (optimal)
CPU Avg. 78 % 42 %
Job Failure Rate 12 % 0 %

Security Considerations

When you raise max_connections and enable persistent connections, you also widen the attack surface. Follow these safeguards:

  • Use strong MySQL passwords and rotate them quarterly.
  • Restrict remote MySQL access to 127.0.0.1 via bind-address.
  • Enable mysql_secure_installation to remove anonymous users.
  • Limit Supervisor commands to the www-data user.
  • Apply fail2ban rules for repeated failed DB logins.

Bonus Performance Tips

Success: Enable Laravel Horizon for real‑time queue metrics and auto‑scaling.

# Install Horizon
composer require laravel/horizon

# Publish config
php artisan horizon:install

# Start Horizon with Supervisor
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/laravel/horizon.log

Additional ideas:

  • Use php artisan optimize after each deploy.
  • Cache config and routes: php artisan config:cache && php artisan route:cache.
  • Leverage Cloudflare “Rate Limiting” to protect API endpoints that feed the queue.
  • Consider Dockerizing the stack; isolate MySQL, Redis, and PHP‑FPM containers with resource limits.

FAQ

Q: My VPS is on cPanel and I can’t edit mysqld.cnf. What now?

A: Use WHM → “SQL Services → MySQL/MariaDB Configuration” to raise max_connections. Then restart MySQL via WHM.

Q: Should I switch from MySQL to MariaDB?

A: MariaDB is a drop‑in replacement and often handles connection spikes better. Test on a staging VPS before production.

Q: How many Redis instances do I need?

A: One instance per server is fine for most apps. Use separate databases (0‑15) for cache vs queues to avoid key collisions.

Final Thoughts

Queue crashes on a cPanel VPS are rarely a “Laravel bug” – they’re a signal that the underlying infrastructure is out of sync with the workload. By aligning MySQL limits, trimming worker counts, and adding Redis for lock handling, you regain control and turn a flaky queue into a reliable background engine.

Take the time to document each change, monitor SHOW GLOBAL STATUS LIKE 'Threads_connected' after deployments, and you’ll spot regressions before they affect customers.

Looking for an ultra‑fast, managed VPS that already ships with Redis, PHP‑FPM, and MySQL tuned for Laravel? Cheap secure hosting on Hostinger can get you up and running in minutes.

No comments:

Post a Comment