MySQL "Too many connections" (error 1040): monitor connection usage and fix it for good
Fix MySQL error 1040, Too many connections: find who is holding connections, raise max_connections safely, and alert before the limit is hit.
By The Opservo team
ERROR 1040 (HY000): Too many connections. When MySQL prints that line, it has hit its hard ceiling on simultaneous clients and is turning every new request away — including the one you would use to see what is wrong. The ceiling is max_connections, it defaults to a surprisingly low 151, and the usual cause is not traffic but connections that were never closed. This guide shows how to read connection usage right now, how to raise the limit without running out of memory, and how to get a warning at 80% so the next spike is a graph, not an outage.
Understanding How MySQL Counts Connections
MySQL tracks two key numbers: the maximum allowed connections (max_connections) and the current count of open connections. The default max_connections value is 151 — low enough to surprise teams who haven't thought about it. Each connection consumes memory (roughly 0.5–1 MB depending on your query buffers), so raising it blindly can exhaust RAM just as fast as the connection limit itself.
There's also a reserved slot: MySQL holds one extra connection above the limit exclusively for accounts with the SUPER privilege or CONNECTION_ADMIN. That's your emergency escape hatch when everything else is locked out.
Check Your Current Connection Status Right Now
Log into MySQL and run these three commands to get an instant picture of where you stand:
- SHOW STATUS LIKE 'Threads_connected'; — current open connections
- SHOW STATUS LIKE 'Max_used_connections'; — peak connections since last restart
- SHOW VARIABLES LIKE 'max_connections'; — your configured ceiling
- SHOW PROCESSLIST; — every active connection with its current state and query
If Max_used_connections is close to max_connections, you've already been near the edge — possibly without knowing it. A safer rule of thumb: keep peak usage below 80% of your limit. If Threads_connected is consistently above that threshold, you need to act before traffic spikes do it for you.
For a more granular look at what's holding connections open, use: SELECT user, host, db, command, time, state FROM information_schema.processlist ORDER BY time DESC; — this surfaces long-running queries and idle connections that are hogging slots.
The Most Common Causes (and Their Fixes)
Before raising max_connections, diagnose why you're running out. The usual culprits are connection leaks, missing connection pooling, and runaway queries that hold connections open far too long.
- No connection pooling: Every PHP-FPM worker, Puma thread, or Node process opens its own connection. Under load, this multiplies fast. Add PgBouncer or ProxySQL in front of MySQL, or enable your framework's built-in pool.
- Connection leaks: Code that opens a connection but doesn't reliably close it (especially on error paths) will drain your pool. Audit try/catch/finally blocks and use connection managers that auto-release.
- Long-running queries: A slow query holds a connection open for its entire duration. Run SHOW FULL PROCESSLIST; and look for anything in the Time column above a few seconds. Kill them with KILL <process_id>; and then fix the underlying query or add an index.
- wait_timeout too high: MySQL waits a long time before closing idle connections. Lower it: SET GLOBAL wait_timeout = 60; — this reclaims idle slots much faster.
- Too many app servers: Horizontal scaling is great until each new instance adds another pool of connections. Size your pools per instance: total_connections = instances × pool_size_per_instance, and make sure that stays under max_connections.
Tuning max_connections Safely
If your usage analysis shows you genuinely need more connections, raise the limit — but calculate it against your available RAM first. A rough formula: available RAM for MySQL ÷ memory per connection. You can estimate per-connection memory with: SELECT ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack ) / 1024 / 1024 AS mb_per_connection;
To change the limit without a restart: SET GLOBAL max_connections = 300; — this takes effect immediately. To make it permanent, add max_connections = 300 to your /etc/mysql/mysql.conf.d/mysqld.cnf (or equivalent) under the [mysqld] section, then verify with SHOW VARIABLES LIKE 'max_connections'; after the next reload.
Raising max_connections is a short-term fix. If you're consistently hitting the limit, pooling and query optimization are the real solution — more headroom just buys you time.
Setting Up Ongoing Monitoring and Alerts
One-off checks aren't enough — you need continuous visibility. If you're running Prometheus, the mysqld_exporter exposes mysql_global_status_threads_connected and mysql_global_variables_max_connections. A simple alert rule: fire when threads_connected / max_connections > 0.75 for more than 5 minutes.
For a lightweight shell-based alert, add this to a cron job running every minute:
- Query Threads_connected and max_connections via mysql -e 'SHOW STATUS...'
- Calculate the ratio with awk or bc
- Send a Slack webhook or email if the ratio exceeds your threshold
- Log Max_used_connections daily so you can spot trends over time
Also track the aborted_connects status variable (SHOW STATUS LIKE 'Aborted_connects';). A rising number here means clients are actively failing to connect — a leading indicator that you're already at capacity during bursts even if your average looks fine.
If you're running multiple services on the same server — MySQL, Nginx, your app — it helps to have a single view across all of them. Opservo monitors your MySQL connection metrics alongside system resources and flags when the combination of high connection count and memory pressure is trending toward trouble, giving you time to act rather than react.
The bottom line: 'too many connections' is almost never a MySQL problem — it's an application architecture problem that MySQL is reporting for you. Get the monitoring in place, add connection pooling, tune wait_timeout, and treat max_connections as a last resort. With these steps in place, that 1040 error becomes a rarity instead of a 2 AM wake-up call.