
Few MySQL errors are as disruptive as this one:
ERROR 1040 (HY000): Too many connections
Your database is running. Your server has RAM to spare. Yet MySQL is refusing new connections and your users are seeing errors.
The cause is almost always the same: the application is opening new connections faster than it is releasing them. Connection pooling is the standard solution — and one that most applications should be using but are not.
Opening a database connection involves more work than most developers expect:



A connection pool maintains a set of open, authenticated connections that are shared across requests. Instead of opening a new connection for every database operation, the application borrows one from the pool, uses it, and returns it.

Application-Level Pooling
Most application frameworks and database libraries include built-in pooling. This is the simplest starting point.
maximumPoolSize: 20
minimumIdle: 5
connectionTimeout: 30000
maxLifetime: 1800000
pool_size: 10
max_overflow: 20
pool_timeout: 30
pool_recycle: 3600
pool_pre_ping: True
The key settings across all frameworks are the same: maximum pool size, idle timeout, connection validation, and connection lifetime.
For microservices or containerized deployments, each application instance maintains its own pool — multiplying total connections as you scale:

This pattern is particularly valuable in containerized environments where instance count scales dynamically.
— Check current limits and usage
SHOW VARIABLES LIKE ‘max_connections’;
SHOW STATUS LIKE ‘Threads_connected’;
SHOW STATUS LIKE ‘Max_used_connections’;

Set the pool’s maxLifetime (or equivalent) shorter than MySQL’s wait_timeout so the pool replaces connections before MySQL closes them on its end.
Bigger is not always better. Research into connection pooling consistently shows that optimal pool sizes for database workloads are smaller than most developers expect.
A useful starting point:

— Who is connected and what are they doing?
SELECT user, host, db, command, time, state
FROM information_schema.processlist
ORDER BY time DESC;
— Connection count by user
SELECT user, host, COUNT(*) AS connections
FROM information_schema.processlist
GROUP BY user, host
ORDER BY connections DESC;
— Were connections refused?
SHOW STATUS LIKE ‘Connection_errors_max_connections’;
A non-zero Connection_errors_max_connections means users received “Too many connections” errors. Investigate what drove connection count that high.
If Threads_connected regularly approaches max_connections , you have a problem developing — either pool configuration needs adjustment or you need a proxy layer.
MONyog tracks connection metrics continuously: active connections versus the configured limit, connection peaks over time, and long-running queries that hold connections without doing useful work. Configure an alert at 80% of max_connections to get advance warning before connections start being refused.
Want to monitor MySQL connections before they become a problem? Start a free MONyog trial — real-time connection metrics and alerts in minutes.
MySQL has a hard limit set by max_connections . When active connections hit this limit, new connection attempts are refused. Common causes include: applications opening connections without pooling, connection leaks where connections are not properly returned, too many application instances each with large pools, or max_connections set too low for the workload.
Start with 10–20 connections for a single instance. More connections than the database can execute concurrently create overhead that hurts more than it helps. Use the formula (CPU cores x 2) + 1 as a starting point, then benchmark under production load and adjust.
Pooled connections can go stale if the database server restarts or a firewall drops idle
connections. Connection validation sends a lightweight check (typically SELECT 1 ) before handing a connection to the application, detecting and replacing dead connections automatically. Enable this in production — it prevents errors from stale connections.
wait_timeout is the MySQL server setting for how long it keeps an idle connection open before closing it. Pool maxLifetime is how long the pool keeps a connection before replacing it. Always set maxLifetime shorter than wait_timeout so the pool recycles connections before MySQL closes them, preventing the pool from handing out dead connections.
You can configure pools to direct read queries to replicas and write queries to the source, but this typically requires a proxy layer to handle routing automatically. A database proxy sits between your application and MySQL, routing traffic based on query type and managing connection pools to multiple MySQL endpoints.
Serverless functions each start fresh, creating a new connection on every invocation. This bypasses application-level pooling entirely and can quickly exhaust max_connections under load. The recommended solution is a proxy-level connection pooler that maintains persistent connections to MySQL while accepting short-lived connections from serverless functions.
Check Threads_connected versus max_connections . If you regularly exceed 80% of max_connections , take action before the remaining 20% is consumed. Also check Max_used_connections to see the historical peak, and Connection_errors_max_connections to confirm whether any connection attempts have already been refused.
Practically, no. Each connection consumes memory — at 2–8 MB per connection, a max_connections of 10,000 would reserve 20–80 GB just for connection overhead. Size max_connections based on available memory after accounting for the InnoDB buffer pool. Pooling reduces the total connections needed rather than raising the ceiling.