Error Medic

MySQL Too Many Connections: Fix ERROR 1040 Fast (All Causes Covered)

Fix MySQL 'Too many connections' ERROR 1040 fast. Covers max_connections tuning, OOM kills, disk full, high CPU, crashed service, and permanent solutions.

Last updated:
Last verified:
2,013 words
Key Takeaways
  • Root cause 1: max_connections limit hit — MySQL rejects new clients with ERROR 1040 when active threads equal max_connections (default 151); fix by raising the limit in my.cnf and reviewing connection pooling.
  • Root cause 2: Connection leaks — application code that opens connections without closing them exhausts the pool over time; inspect SHOW PROCESSLIST for sleep threads older than wait_timeout.
  • Root cause 3: Cascading failures — a slow query causing high CPU or a disk-full event triggers OOM kills, service crashes, or 502 errors upstream; always check disk space, memory, and slow query log together.
  • Quick fix summary: Run `SET GLOBAL max_connections = 500;` for immediate relief, then audit open connections with `SHOW STATUS LIKE 'Threads_connected';`, fix the leak in application code, and make the change permanent in /etc/mysql/my.cnf.
Fix Approaches Compared
MethodWhen to UseTime to ApplyRisk
SET GLOBAL max_connections (live)Immediate relief without restart< 1 minLow — temporary, survives until restart
Edit my.cnf + restart MySQLPermanent fix after root cause identified2–5 minLow — causes brief downtime during restart
Kill idle/sleeping connectionsConnection pool exhausted by leaks1–2 minLow — only kills SLEEP state threads
Enable & tune connection pooling (ProxySQL / PgBouncer-style)High-traffic apps with connection spikes30–60 minMedium — requires application config changes
Free disk space / expand volumeMySQL crashed or won't start due to disk full5–30 minLow-Medium — data at risk if binlogs are purged
Increase OS open-file limits (ulimit)MySQL hits OS fd limits before MySQL limits5 min + restartLow — system-level change
Tune wait_timeout / interactive_timeoutMany long-lived sleeping connections2 min + reloadLow — may break apps relying on persistent connections

Understanding the MySQL Too Many Connections Error

When MySQL returns ERROR 1040 (HY000): Too many connections, it means every slot in the connection pool (controlled by max_connections) is occupied and the server cannot accept another TCP connection. The client — whether your application, a cron job, or a CLI tool — receives the error immediately without ever executing a query.

This error surfaces in many forms depending on your stack:

  • Raw MySQL client: ERROR 1040 (HY000): Too many connections
  • PHP/Laravel: SQLSTATE[HY000] [1040] Too many connections
  • Python/Django: django.db.utils.OperationalError: (1040, 'Too many connections')
  • Node.js/mysql2: Error: Too many connections
  • HTTP layer (nginx → app → MySQL): 502 Bad Gateway when the application itself crashes or hangs waiting for a DB handle

Step 1: Confirm the Diagnosis

SSH into the MySQL host and check current connection state:

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

If Threads_connected equals max_connections, you have confirmed the error. Max_used_connections tells you the historical peak — if it consistently matches max_connections, the limit is too low or there is a leak.

Check for sleeping (leaked) connections:

SELECT user, host, db, command, time, state
FROM information_schema.processlist
WHERE command = 'Sleep'
ORDER BY time DESC
LIMIT 30;

A large number of rows with Command = Sleep and high Time values (hundreds or thousands of seconds) indicates connection leaks.


Step 2: Immediate Relief — Raise max_connections Live

This does not require a MySQL restart and takes effect instantly:

SET GLOBAL max_connections = 500;

Choose a value based on available RAM. A rough formula is:

max_connections ≈ (Available RAM in MB - global buffer RAM) / per_connection_RAM

Each connection uses approximately 1–8 MB depending on sort_buffer_size, join_buffer_size, and tmp_table_size. On a server with 8 GB RAM allocating 4 GB to InnoDB buffer pool, a safe ceiling is 300–500 connections. Setting it too high (e.g., 10 000) can cause the MySQL process itself to OOM-kill.


Step 3: Kill Stale Sleeping Connections

If the pool is already full before you can raise the limit, kill sleeping threads:

-- Generate KILL statements for all sleeping connections older than 300 seconds
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300;

Copy the output and run each KILL <id>; statement, or use a shell loop:

mysql -u root -p -e "
  SELECT CONCAT('KILL ', id, ';')
  FROM information_schema.processlist
  WHERE command = 'Sleep' AND time > 300;
" --skip-column-names | mysql -u root -p

Step 4: Make the Fix Permanent in my.cnf

Edit /etc/mysql/mysql.conf.d/mysqld.cnf (Ubuntu/Debian) or /etc/my.cnf (RHEL/CentOS):

[mysqld]
max_connections        = 500
wait_timeout           = 300
interactive_timeout    = 300
connect_timeout        = 10

wait_timeout and interactive_timeout control how long MySQL keeps an idle connection open. Dropping them from the default 28800 seconds (8 hours) to 300 seconds (5 minutes) reclaims leaked connections automatically.

Reload without full restart (MySQL 8.0+):

mysqladmin -u root -p reload
# or for a clean restart:
systemctl restart mysql

Step 5: Investigate MySQL Crash, OOM, and Disk Full

MySQL not starting / crashed:

journalctl -u mysql -n 100 --no-pager
cat /var/log/mysql/error.log | tail -100

Common crash signatures:

  • [ERROR] InnoDB: ib_logfile0 size ... is not equal to innodb_log_file_size — log file size mismatch after config change.
  • [ERROR] Can't start server: Bind on TCP/IP port. Got error: 98: Address already in use — port conflict; check ss -tlnp | grep 3306.
  • Out of memory (oom_kill_score ...) in dmesg — MySQL OOM-killed by the kernel.

Disk full causing MySQL failure:

df -h /var/lib/mysql
du -sh /var/lib/mysql/*
# Free space by purging old binary logs safely:
mysql -u root -p -e "PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;"

Never manually delete .ibd or ib_logfile* files. Only purge binary logs via MySQL's own PURGE BINARY LOGS command.

High CPU / slow queries:

# Enable slow query log temporarily
mysql -u root -p -e "
  SET GLOBAL slow_query_log = 'ON';
  SET GLOBAL long_query_time = 1;
  SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
"
# Then analyze:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

High CPU combined with many connections usually means N+1 query patterns, missing indexes, or a runaway reporting query. Use SHOW PROCESSLIST to identify the offending query and EXPLAIN to optimize it.


Step 6: Long-Term Fix — Connection Pooling

For applications with bursty traffic, implement a connection pooler in front of MySQL:

  • ProxySQL — transparent proxy with advanced connection multiplexing, query routing, and query caching. Recommended for production MySQL setups.
  • MySQL Router — lightweight official router suitable for InnoDB Cluster topologies.
  • Application-level pooling — SQLAlchemy pool_size + max_overflow, HikariCP for Java, mysql2 pool in Node.js.

With ProxySQL, 1000 application connections can map to 50 real MySQL connections, effectively multiplying your capacity without increasing server load.


Step 7: Monitor to Prevent Recurrence

Add these metrics to your monitoring stack (Prometheus + mysqld_exporter, Datadog, or New Relic):

  • mysql_global_status_threads_connected — alert at 80% of max_connections
  • mysql_global_status_connection_errors_max_connections — should be 0
  • mysql_global_status_aborted_connects — rising values indicate connection failures
  • Disk usage on /var/lib/mysql — alert at 75% full

A Prometheus alert rule:

- alert: MySQLTooManyConnections
  expr: |
    mysql_global_status_threads_connected /
    mysql_global_variables_max_connections > 0.8
  for: 2m
  labels:
    severity: warning
  annotations:
    summary: "MySQL connections > 80% of max_connections"

Frequently Asked Questions

bash
#!/usr/bin/env bash
# MySQL Too Many Connections — Diagnostic & Fix Script
# Run as root or a MySQL user with SUPER / PROCESS privileges
# Usage: bash mysql_conn_fix.sh [mysql_root_password]

MYSQL_PASS="${1:-}"
MYCLI="mysql -u root ${MYSQL_PASS:+-p$MYSQL_PASS}"

echo '=== 1. CURRENT CONNECTION STATE ==='
$MYCLI -e "SHOW STATUS LIKE 'Threads_connected';"
$MYCLI -e "SHOW STATUS LIKE 'Max_used_connections';"
$MYCLI -e "SHOW VARIABLES LIKE 'max_connections';"

echo ''
echo '=== 2. SLEEPING CONNECTIONS (potential leaks) ==='
$MYCLI -e "
  SELECT user, host, db, time AS sleep_seconds
  FROM information_schema.processlist
  WHERE command = 'Sleep'
  ORDER BY time DESC
  LIMIT 20;
"

echo ''
echo '=== 3. LONG-RUNNING QUERIES (> 30 seconds) ==='
$MYCLI -e "
  SELECT id, user, host, db, time, state, LEFT(info, 80) AS query
  FROM information_schema.processlist
  WHERE command != 'Sleep' AND time > 30
  ORDER BY time DESC;
"

echo ''
echo '=== 4. DISK SPACE CHECK ==='
df -h /var/lib/mysql

echo ''
echo '=== 5. MYSQL ERROR LOG (last 30 lines) ==='
MYSQL_LOG=$(mysql -u root ${MYSQL_PASS:+-p$MYSQL_PASS} \
  -se "SELECT @@global.log_error" 2>/dev/null)
tail -30 "${MYSQL_LOG:-/var/log/mysql/error.log}" 2>/dev/null || \
  journalctl -u mysql -n 30 --no-pager

echo ''
echo '=== 6. OOM EVENTS IN KERNEL LOG ==='
dmesg | grep -iE 'oom|kill|mysql' | tail -20

echo ''
echo '=== 7. IMMEDIATE FIX: raise max_connections to 500 ==='
read -rp 'Apply live fix (SET GLOBAL max_connections=500)? [y/N] ' confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
  $MYCLI -e "SET GLOBAL max_connections = 500;"
  echo 'Done. Remember to persist in /etc/mysql/mysql.conf.d/mysqld.cnf:'
  echo '  [mysqld]'
  echo '  max_connections = 500'
  echo '  wait_timeout    = 300'
  echo '  interactive_timeout = 300'
fi

echo ''
echo '=== 8. KILL SLEEPING CONNECTIONS OLDER THAN 300s ==='
read -rp 'Kill stale sleeping connections? [y/N] ' kill_confirm
if [[ "$kill_confirm" =~ ^[Yy]$ ]]; then
  $MYCLI --skip-column-names -e "
    SELECT CONCAT('KILL ', id, ';')
    FROM information_schema.processlist
    WHERE command = 'Sleep' AND time > 300;
  " | $MYCLI
  echo 'Stale connections killed.'
fi

echo ''
echo '=== DONE ==='  
E

Error Medic Editorial

Error Medic Editorial is a team of senior DevOps and SRE engineers with 10+ years of experience operating MySQL, PostgreSQL, and distributed databases at scale. We write production-focused troubleshooting guides drawn from real incident post-mortems, Stack Overflow answers, and official vendor documentation.

Sources

Related Guides