MySQL "Too Many Connections" Error: Causes and the Fix

· 6 min read · 25 views · Getwebup

Your site is fine one minute, then MySQL starts throwing Error establishing a database connection or, if you're looking at the raw log, the far more honest ERROR 1040 (HY000): Too many connections. It usually shows up during a traffic spike, a cron pile-up, or right after you add a new site to a busy server. Here's what's actually happening and how to fix it without just restarting MySQL and hoping.

What "too many connections" actually means

MySQL caps how many clients can be connected to it at once. That cap is the max_connections variable, and it exists to protect the server from being overwhelmed — each open connection reserves memory, and MySQL would rather refuse a new connection than let the whole server run out of RAM and crash.

When every connection slot is already in use, the next request — your WordPress site loading a page, a WP-CLI command, phpMyAdmin, whatever — gets rejected. WordPress doesn't know the difference between "wrong password" and "no room at the table," so it shows the same generic database connection error either way. The real cause only shows up in the MySQL error log or when you check the variable directly.

Confirm it's actually a connection limit, not something else

Don't change any config until you've confirmed this is really the issue. SSH in (or use cPanel's Terminal) and run:

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

If Threads_connected is sitting close to or at max_connections, that's your answer. You can also check SHOW STATUS LIKE 'Connection_errors_max_connections'; — a rising, non-zero number confirms connections are actually being refused, not just running high.

On shared cPanel hosting you usually don't have root MySQL access. In that case check cPanel → Metrics → MySQL/MariaDB if your host exposes it, or open a support ticket and ask them to check Threads_connected vs max_connections for your account.

See what's actually holding connections open

If you do have access, this tells you who's eating the slots:

mysql -u root -p -e "SHOW FULL PROCESSLIST;"

Look at the Command and Time columns. A handful of things commonly show up here:

  • Sleep connections sitting for a long time — usually PHP-FPM or Apache keeping connections open longer than they need to, or a plugin not closing its own queries.
  • Many identical queries from the same host — a runaway cron job, a bot hammering a search page, or a WooCommerce/booking plugin polling the database too aggressively.
  • Locked queries — a slow, unindexed query holding a table lock while dozens of others queue up behind it.

Fixes, from fastest to most durable

1. Clear the immediate backlog

If the site is down right now and you need it back fast, restarting MySQL closes every stale connection at once:

sudo systemctl restart mysql
# or, depending on the distro:
sudo systemctl restart mariadb

This buys you time, not a fix. If nothing else changes, you'll be back here within hours or days.

2. Raise max_connections — carefully

Each connection can use a meaningful chunk of RAM depending on your buffer settings, so don't just set this to 1000 on a 2GB VPS. A safe starting point is 150–200 on a small VPS, higher on larger boxes. Edit your MySQL config:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# add or edit under [mysqld]:
max_connections = 200

Restart the service afterward. On cPanel/WHM servers, this lives in WHM → MySQL/MariaDB Configuration instead of editing the file directly — use that UI so your change survives the next cPanel update.

3. Fix the thing actually consuming connections

Raising the limit without addressing the cause just delays the next outage. The usual culprits, in order of how often we see them:

  • PHP-FPM pool sized larger than MySQL can handle. If pm.max_children in your PHP-FPM pool config is 100 and each child can open a DB connection, you can hit 100 connections from PHP alone. Check /etc/php/8.x/fpm/pool.d/www.conf and bring pm.max_children in line with what your MySQL max_connections and available RAM can actually support.
  • A plugin or cron job not closing connections. WP-Cron tasks, backup plugins, and import/export tools are common offenders. Check wp-content/plugins for anything that runs on a tight schedule, and consider moving WP-Cron to a real system cron job so it doesn't fire on every page load.
  • Persistent connections left on. If DB_HOST in wp-config.php is set to something like p:localhost, that colon-prefixed "p:" enables persistent MySQL connections, which don't get released between requests. Remove the p: unless you specifically configured it that way for a reason.
  • Too many sites on one small database server. Each WordPress install on shared MySQL adds its own baseline connection load. If you've added several sites to a small VPS, the fix isn't a config tweak — it's more RAM or a managed database tier.

4. Set a sane wait_timeout

A long wait_timeout lets idle connections sit around doing nothing while still holding a slot. Lowering it clears dead weight faster:

# in mysqld.cnf, under [mysqld]:
wait_timeout = 180
interactive_timeout = 180

180 seconds is a reasonable default for a web app. Don't set this too low if you run long batch jobs or big phpMyAdmin imports — you'll cut those off mid-run instead.

SymptomLikely causeWhere to look
Error appears only during traffic spikesmax_connections too low for real loadSHOW VARIABLES / WHM MySQL config
Error is constant, even at low trafficSomething holding connections openSHOW FULL PROCESSLIST
Started right after adding a pluginPlugin not closing DB connections / polling too oftenDeactivate plugins one by one
Multiple sites on the box affected togetherServer-wide connection ceiling hitPHP-FPM pool size vs max_connections

Prevention: keep it from coming back

  • Monitor Threads_connected over time, not just when things break. Most VPS monitoring tools (or a simple cron script piping to a log) will show you the trend before it becomes an outage.
  • Cap PHP-FPM's pm.max_children to something your server's RAM and MySQL's max_connections can both support — don't size it in isolation.
  • Move WP-Cron off page-load triggering (define('DISABLE_WP_CRON', true); in wp-config.php, paired with a real system cron hitting wp-cron.php every few minutes).
  • Add basic caching (object cache or a page cache) so repeat requests don't all hit MySQL individually.
  • If you're consistently near the ceiling on a VPS, that's a sizing signal, not just a config one — more RAM usually solves this more permanently than tuning alone.

On Getwebup VPS and cPanel plans, our support team can check Threads_connected, PHP-FPM pool sizing, and slow query logs together, since these three usually need to be tuned as a set rather than one at a time.

Questions people actually ask