ERROR 2006 (HY000): MySQL server has gone away means your client tried to use a connection the server had already closed:
ERROR 2006 (HY000): MySQL server has gone away
The related error you may see instead is:
ERROR 2013 (HY000): Lost connection to MySQL server during query
The distinction matters: 2006 means the client could not even send its request, while 2013 means the request went out and no complete answer came back. 2013 points more strongly at a crash or a break mid-query.
This message is a symptom with several unrelated causes. The fastest route to a fix is to work out which one you have before changing any setting.
Why This Happens — Narrow It Down First
Answer one question: does it fail instantly and reproducibly on the same statement, or unpredictably after a period of inactivity?
| Symptom | Most likely cause | Section |
|---|---|---|
| Same statement fails every time, immediately | Statement exceeds max_allowed_packet | Fix 1 |
| First query after a quiet period fails; a retry works | wait_timeout closed an idle connection | Fix 2 |
| Many connections fail at once, across applications | Server restarted or was killed | Fix 3 |
| Fails part-way through a long query | net_read_timeout / net_write_timeout, or a crash | Fix 4 |
| One connection dies while others are fine | Thread was killed | Fix 5 |
Fix 1: Statement Larger Than max_allowed_packet
If a single statement — a bulk INSERT, a large BLOB, a long IN (...) list — exceeds max_allowed_packet, the server closes the connection instead of replying. This is the most common cause of a reproducible 2006.
SELECT @@global.max_allowed_packet, @@session.max_allowed_packet;
The MySQL 8 default is 64MB, but managed platforms and older versions often ship far lower values. The smaller of the client and server values wins, so raising it on one side alone does nothing.
Set it on the server in an option file so it survives restarts:
# /etc/mysql/mysql.conf.d/mysqld.cnf (path varies by distribution)
[mysqld]
max_allowed_packet = 128M
You can also raise it at runtime without a restart, though it applies to new connections only:
SET GLOBAL max_allowed_packet = 134217728; -- 128MB, resets on restart
Then set the matching client or driver value — --max-allowed-packet for the CLI, or the driver's equivalent option.
Size it to your largest legitimate statement plus headroom, not to the maximum. The setting exists to bound how much memory a single statement can force the server to allocate, so a very large value on a busy server is a memory-pressure risk. Where you control the code, the better fix is to send less: split bulk inserts into batches and store large objects outside the row.
Fix 2: Idle Connection Closed by wait_timeout
The server closes connections that have been idle for wait_timeout — 8 hours by default. Long-lived pooled connections, cron jobs, and worker processes that sit between statements all run into this.
SHOW GLOBAL VARIABLES LIKE '%timeout%';
Look at wait_timeout (non-interactive clients) and interactive_timeout (interactive ones such as the CLI).
The right fix is on the client. A connection pool should never hand out a connection it has not validated:
- Validation query — the pool runs
SELECT 1before lending a connection, discarding dead ones. - Max lifetime — set the pool's maximum connection lifetime below the server's
wait_timeoutso the pool retires connections before the server closes them. - Idle eviction — close connections that have been idle beyond a threshold.
Raising wait_timeout instead means more idle connections held open against max_connections, which trades this error for a different one. Only raise it when you have a specific workload that genuinely needs long idle periods.
The mysql command-line client reconnects automatically, which is why this cause often looks invisible from the CLI and obvious from the application.
Fix 3: The Server Restarted or Was Killed
If many connections fail simultaneously across different applications, suspect the server rather than any one client.
SHOW GLOBAL STATUS LIKE 'Uptime';
If Uptime is smaller than the age of your connection, the server restarted underneath you. Confirm what happened:
# Did the kernel's OOM killer take mysqld?
dmesg -T | grep -i -E "oom|killed process"
# Was it a clean shutdown or a crash?
sudo systemctl status mysql
Then read the error log, which records shutdowns, signals, and startup — see where MySQL logs are stored for the path on your platform. An OOM kill usually means innodb_buffer_pool_size plus per-connection buffers times max_connections exceeds available RAM; a crash on a specific query means the query is the trigger and belongs in a bug report.
Fix 4: Timeouts During a Long Query
net_read_timeout and net_write_timeout govern how long the server waits on a network read or write while a statement is in flight. A slow client that cannot consume a large result set fast enough can trip these.
SHOW GLOBAL VARIABLES LIKE 'net_%timeout';
Before raising them, rule out a crash — a server that died mid-query produces the same symptom, and increasing a timeout will not fix that. Check Uptime first as in Fix 3. If the server is genuinely up and the client is genuinely slow, streaming the result set rather than buffering it whole is usually a better fix than a longer timeout.
Fix 5: The Thread Was Killed
A KILL statement, an administrative script, or a proxy enforcing its own limits will produce 2006 on exactly one connection while everything else is healthy. Check whether something is killing long-running threads on a schedule — many managed platforms and connection proxies do this by default, and their idle or query timeout is frequently shorter than MySQL's own.
Verify the Fix
Confirm the values actually in force, and that they agree on both sides:
SELECT @@global.max_allowed_packet AS server_packet,
@@global.wait_timeout AS wait_timeout,
@@global.interactive_timeout AS interactive_timeout;
SHOW GLOBAL STATUS LIKE 'Uptime';
SHOW GLOBAL STATUS LIKE 'Aborted_clients';
Aborted_clients climbing steadily is the signature of connections being closed rather than closed cleanly by the application. Watch it over a few minutes rather than reading it once.
Then reproduce the original failure. For a packet-size problem, run the exact statement that failed. For an idle timeout, leave a pooled connection idle past the old threshold and use it again.
Read the Server's Side of the Story
The client only knows the connection vanished; the server knows why. Set verbose logging so aborted connections are recorded with a reason:
SET GLOBAL log_error_verbosity = 3;
Then look for Aborted connection lines naming the user, host, and cause. Distinguishing "got timeout reading communication packets" from "got an error reading communication packets" from a shutdown message tells you which of the fixes above applies, without guessing. See where MySQL logs are stored for where to find it.
Prevent It Coming Back
- Validate pooled connections and keep the pool's max lifetime below the server's
wait_timeout. This alone eliminates the most common recurring case. - Batch large writes instead of raising
max_allowed_packetto cover a single enormous statement. - Keep
max_allowed_packetconsistent between server and client configuration, and set it in an option file so a restart does not silently revert it. - Alert on
Aborted_clientsso you see the trend before users report failures. - Size memory against
max_connectionsso the server is not one traffic spike away from an OOM kill. - Handle reconnection in application code. Even a perfectly tuned server restarts for patching; a pool that retries once on a dead connection turns an outage into a blip.
Next Steps
- Where are MySQL logs stored — the aborted-connection reason lives here
- Fix "Lock wait timeout exceeded" if queries stall rather than disconnect
- Fix ERROR 1045 Access denied if reconnection fails on authentication