Databases

Fix MySQL Too Many Connections Errors

Diagnose MySQL error 1040, find which clients are holding connections open, raise max_connections safely and tune wait_timeout to stop the leak coming back.

IntermediateLinux server7 min read

MySQL error 1040 "Too many connections" means the server has reached its max_connections limit and is refusing new clients. This guide shows you how to confirm the limit, find which users and hosts are holding connections, raise the limit without starving the server of memory, and fix the leak that caused it.

The fix is usually two parts: a short-term change so your application can connect again, and a longer-term change to wait_timeout or your application's connection handling so the same thing does not happen next week.

Before you start

You need:

  • SSH access to your Linux VPS and a user with sudo.
  • The MySQL or MariaDB root password (or another account with SUPER or CONNECTION_ADMIN).
  • A note of which config file your distribution uses. On Ubuntu and Debian it is /etc/mysql/mysql.conf.d/mysqld.cnf. On AlmaLinux and Rocky it is /etc/my.cnf.d/mysql-server.cnf. Check with mysqld --verbose --help | grep -A1 "Default options" if you are unsure.

If MySQL is refusing every connection, including yours, restart it from your VPS provider's console or over SSH with sudo systemctl restart mysql (use mariadb on MariaDB hosts). That clears the current sessions and buys you time to make the real change.

Warning: Restarting MySQL drops all open connections and any in-flight transactions. Do it during a quiet period if the database is live.

Confirm the MySQL too many connections error

First, prove the limit is what you think it is. MySQL keeps one spare connection for accounts with SUPER or CONNECTION_ADMIN, so root can usually still get in.

Bash
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections'; SHOW STATUS LIKE 'Threads_connected'; SHOW STATUS LIKE 'Max_used_connections';"

Read the output like this:

  • max_connections is the ceiling. The default is 151 on MySQL 8 and MariaDB.
  • Threads_connected is how many clients are connected right now.
  • Max_used_connections is the high-water mark since the last restart. If it equals max_connections, you have hit the ceiling.

You can also check the error log for the exact message, which helps confirm whether it is a connection count problem or a thread creation problem:

Bash
sudo tail -n 50 /var/log/mysql/error.log

If you see Can't create a new thread (errno 11) instead of error 1040, the limit is the operating system's process or file-descriptor limit, not max_connections. Check it with:

Bash
systemctl show mysql | grep -i limitnofile
ulimit -u

Find who is holding the connections

Raising the limit without knowing who is connected just delays the next outage. Open a session and look at the process list:

Bash
mysql -u root -p
SQL
SELECT id, user, host, db, command, time, state, LEFT(info, 80) AS query
FROM information_schema.processlist
ORDER BY time DESC
LIMIT 25;

Rows with command = Sleep and a large time value are the usual culprit: an application opened a connection and never closed it. Group them to see which host or user is responsible:

SQL
SELECT user, host, COUNT(*) AS conns
FROM information_schema.processlist
GROUP BY user, host
ORDER BY conns DESC;

If one web server or one cron job owns hundreds of sleeping sessions, fix that client rather than the server. Common causes are a PHP script using mysqli_connect inside a loop, a Python worker that never calls close, or a pool size set higher than the number of workers.

You can kill a specific runaway session, but only if you are sure it is idle:

SQL
KILL 12345;

Warning: KILL on an active session rolls back its current transaction. Never kill sessions in bulk on a production database without checking what they are doing first.

Raise max_connections safely

For an immediate fix, change the value at runtime. This survives until the next restart:

SQL
SET GLOBAL max_connections = 200;

To make it permanent, edit the server config. On Ubuntu or Debian:

Bash
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add or change these lines under [mysqld]:

INI
[mysqld]
max_connections = 200
thread_cache_size = 32

On AlmaLinux or Rocky, the same block goes in /etc/my.cnf.d/mysql-server.cnf. Then restart:

Bash
sudo systemctl restart mysql

Warning: Every connection has its own buffers. Raising max_connections from 151 to 1000 on a small VPS can push MySQL into swap or trigger the OOM killer. Increase it in steps and watch memory with free -m under normal load. See Tune MySQL and MariaDB memory settings for a small VPS before you go large.

thread_cache_size keeps idle threads around so new connections do not pay the cost of creating one. A value between 16 and 64 is reasonable for most VPS workloads.

Fix connection leaks and tune wait_timeout

The default wait_timeout is 28800 seconds — eight hours. That means a forgotten connection occupies a slot for most of a day. For a typical web application, lower it:

INI
[mysqld]
wait_timeout = 300
interactive_timeout = 300

wait_timeout applies to non-interactive clients such as PHP and Python. interactive_timeout applies to the mysql command-line client. Setting both keeps behaviour predictable.

Tip: If your application uses persistent connections or a pool, set wait_timeout slightly higher than the pool's idle timeout. Otherwise the pool hands out connections the server has already closed, and users see "MySQL server has gone away".

On the application side, the durable fixes are:

  • Use a connection pool with a fixed maximum size, and make sure that maximum multiplied by the number of app workers stays below max_connections.
  • Close connections in a finally block or use a context manager so an exception cannot leak one.
  • Avoid opening a new connection per request when a pool is available.

Apply the config change with a restart, then confirm the new values:

Bash
sudo systemctl restart mysql
mysql -u root -p -e "SHOW VARIABLES WHERE Variable_name IN ('max_connections','wait_timeout','interactive_timeout','thread_cache_size');"

Check it works

Put the server under realistic load and watch the numbers:

Bash
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected'; SHOW STATUS LIKE 'Max_used_connections'; SHOW STATUS LIKE 'Aborted_connects';"

You want Threads_connected to stay well below max_connections at peak, and Max_used_connections to stop tracking the ceiling. Aborted_connects should stay flat — a rising value means clients are being rejected or failing to authenticate.

Then confirm your application connects normally and that no error 1040 appears in its logs. If you use a monitoring check, point it at SHOW STATUS LIKE 'Threads_connected' and alert at around 80 percent of the limit.

Troubleshooting

  • Error 1040 returns within minutes of raising the limit — a client is leaking connections faster than you can raise the ceiling. Re-run the grouped process list query and fix the offending host or user instead of increasing max_connections again.
  • Can't create a new thread (errno 11) — the OS limit, not max_connections. Raise LimitNOFILE for the service with a systemd drop-in and check ulimit -u for the MySQL user.
  • "MySQL server has gone away" after lowering wait_timeout — your pool is holding connections longer than the server allows. Raise wait_timeout above the pool's idle timeout, or enable the pool's connection-validation query.
  • Root cannot connect either — the reserved connection is already in use. Restart the service from the console, then set a sane max_connections before the app reconnects.
  • Connections pile up only during backups — mysqldump holds a long-running session. Schedule backups off-peak and see Automating MySQL backups with mysqldump and cron.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.