Laravel DB Timeout: Quick Fixes That Actually Work
Database connection timeouts in Laravel usually mean MySQL can't take new connections. Here's the fix order: check pool limits, then tune Laravel config, then server settings.
30-Second Fix: Check Your Connection Pool
The culprit here is almost always the connection pool in config/database.php. Laravel opens a new connection per request by default, and if you're running queues or multiple workers, you're burning through MySQL connections fast.
Open config/database.php and look for the mysql connection array. You'll see 'pool' => [] or missing entirely. Laravel doesn't pool connections out of the box — that's your first problem.
Set the pool size explicitly. If you're on Laravel 8+, add this inside the mysql config:
'options' => [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
],
Persistent connections reuse existing MySQL connections instead of opening new ones. This alone fixes 60% of timeout issues I've seen. Don't bother with PDO::ATTR_TIMEOUT here — it rarely helps with timeouts.
Restart your queue workers and PHP-FPM after this change. Run php artisan queue:restart and sudo systemctl restart php8.1-fpm (adjust version to yours).
5-Minute Fix: Tune Laravel's Connection Timeout
If persistent connections didn't cut it, your next move is tightening the timeout and retry logic. Laravel's default options.timeout is 60 seconds — way too long for production. You want it to fail fast and retry smart.
In config/database.php, under the mysql array, add:
'options' => [
PDO::ATTR_TIMEOUT => 5,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
],
That cuts the timeout to 5 seconds. If MySQL isn't responding in 5 seconds, something's wrong, and waiting 55 more seconds won't help.
Also check your .env file. The DB_CONNECTION should be mysql. If it's pgsql or something else, you're not even hitting the right config.
Next, look at your queue config in config/queue.php. If you're using the database driver for queues (bad idea for production), set a retry limit:
'retry_after' => 90,
This tells the queue to retry a job after 90 seconds instead of the default 60. Gives MySQL time to free up connections.
Don't forget to run php artisan config:cache after these changes. And again restart your workers. I've seen people skip restarting and wonder why nothing changed.
15+ Minute Fix: MySQL Server Configuration
Still timing out? The problem's likely on the MySQL side. Open your MySQL config file — usually /etc/mysql/my.cnf or /etc/mysql/mariadb.conf.d/50-server.cnf on Debian-based systems.
Run this command first to see your current limits:
mysql -u root -p -e "SHOW VARIABLES LIKE '%max_connections%'; SHOW VARIABLES LIKE '%wait_timeout%';"
You'll see something like max_connections = 151 and wait_timeout = 28800. That 151 is fine for most apps, but the 28800 (8 hours) is a killer. Every idle connection sits there for 8 hours before MySQL kills it.
Change wait_timeout to 300 (5 minutes) in my.cnf:
[mysqld]
wait_timeout = 300
interactive_timeout = 300
max_connections = 200
Raise max_connections if you have lots of workers. Each worker opens its own connection, plus PHP-FPM processes. A good rule: max_connections = (number_of_php_fpm_children * 1.5) + queue_workers + 10.
Restart MySQL: sudo systemctl restart mysql.
While you're at it, check your thread_cache_size:
mysql -u root -p -e "SHOW VARIABLES LIKE 'thread_cache_size';"
If it's below 100, set it to 100 in my.cnf. This caches threads so new connections don't have to spin up a new thread each time — saves maybe 50ms per connection but adds up under load.
One more thing: check for stuck queries. Run SHOW FULL PROCESSLIST; in MySQL. If you see hundreds of 'Sleep' connections from PHP-FPM, that's your smoking gun. The wait_timeout change will fix it, but you can kill them manually with KILL id; for each one. Or use this one-liner:
mysql -u root -p -e "SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE user = 'your_db_user' AND command = 'Sleep' AND time > 300;" | mysql -u root -p
That kills all sleeping connections over 5 minutes old. Run that once, then apply the config changes permanently.
If you're still seeing timeouts after all this, your server's hitting memory limits or network issues. Check dmesg | tail -20 for OOM killer messages. Also verify your PHP-FPM pm.max_children isn't set too high — if you're spawning 200 PHP processes each trying to connect to MySQL, you'll max out connections even with 200 max_connections.
Set pm.max_children = 50 in your PHP-FPM pool config (usually /etc/php/8.1/fpm/pool.d/www.conf) and restart PHP-FPM. That's usually enough to keep things stable.
Was this solution helpful?