Fixing the '2006 MySQL Server Has Gone Away' Error in PHP PDO

intermediate🐘 PHP2026-05-28| PHP 7.4/8.x, MySQL 5.7/8.0, MariaDB, Linux (Ubuntu/CentOS), Docker

Error Message

PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
#php#pdo#mysql#database#connection#timeout

The Problem: An Abrupt DisconnectYou are running a 2:00 AM cron job to sync thousands of inventory items, and halfway through, the process grinds to a halt. Your logs show the infamous 2006 MySQL server has gone away error. It is confusing because the database is still running, and you can log in via the terminal without issues. Essentially, the MySQL server closed the door on your script, but your PHP code is still trying to use an expired connection handle.

Why Does the Server Hang Up?MySQL usually terminates the connection for three specific reasons. First, your query might be too large. If you try to send a 50MB PDF to a server with a 16MB max_allowed_packet limit, the server severs the link immediately.

Second, your script might be too slow between database calls. If your PHP code spends 20 minutes processing data before the next query, MySQL’s wait_timeout expires and kills the idle connection. Finally, the MySQL service itself might have restarted or been terminated by the Linux Out of Memory (OOM) killer due to high RAM usage.

Configuration Fixes: Tuning the EngineThe quickest way to handle large data imports is to increase the packet size and timeout limits in your my.cnf or my.ini file. These settings determine the maximum data size and idle time the server tolerates.

1. Increase max_allowed_packetIf you perform bulk inserts—such as 5,000 rows in a single statement—the default 16MB limit is often insufficient. Bump this to 128MB or 256MB to provide more headroom.

# Located in /etc/mysql/my.cnf or /etc/my.cnf
[mysqld]
max_allowed_packet = 128M

2. Extend wait_timeoutFor background workers that perform heavy computations between database updates, increase the idle timeout. While the default is often 28,800 seconds (8 hours), managed environments like AWS RDS may set this significantly lower.

[mysqld]
wait_timeout = 28800
interactive_timeout = 28800

After saving these changes, apply them by restarting the service:

sudo systemctl restart mysql

The Permanent Fix: PHP Reconnection LogicConfiguration changes are helpful, but they are not foolproof. On a flaky network or during long-running tasks, you must assume the connection will eventually drop. Since PHP’s PDO does not reconnect automatically, you need a wrapper to detect a dead link and restore it.

The 'Ping' MethodBefore running a critical query inside a loop, check if the connection is still alive. You can do this by executing a lightweight SELECT 1 query. If it fails with the 2006 error code, catch the exception and instantiate a new PDO object.

/**
 * Ensures the PDO connection is active before proceeding
 */
function getActiveConnection($pdo, $dsn, $user, $pass, $options) {
    try {
        $pdo->query('SELECT 1');
    } catch (PDOException $e) {
        // Check specifically for the 'Gone Away' error
        if (strpos($e->getMessage(), '2006') !== false) {
            return new PDO($dsn, $user, $pass, $options);
        }
        throw $e;
    }
    return $pdo;
}

Handling Batch ProcessingWhen iterating through massive datasets, do not keep a stale connection open if there is a long delay between steps. Wrap your execution in a try-catch block to handle retries dynamically.

foreach ($largeDataSet as $data) {
    try {
        $stmt = $pdo->prepare("INSERT INTO logs (message) VALUES (?)");
        $stmt->execute([$data['message']]);
    } catch (PDOException $e) {
        if ($e->errorInfo[1] == 2006) {
            // Re-establish connection and retry the specific insert
            $pdo = new PDO($dsn, $user, $pass, $options);
            $stmt = $pdo->prepare("INSERT INTO logs (message) VALUES (?)");
            $stmt->execute([$data['message']]);
        } else {
            throw $e;
        }
    }
}

Infrastructure and Environment ChecksIf the error persists despite your code changes, the issue likely sits at the OS or container level. Check the MySQL error log using tail -f /var/log/mysql/error.log. If you see 'mmap can't allocate' or 'Out of memory' messages, your server is running out of physical RAM. In Docker environments, ensure your container has sufficient memory limits; MySQL 8.0 is particularly memory-intensive and may crash if restricted to less than 1GB of RAM.

Verification: Testing the SolutionTo verify your max_allowed_packet fix, try to select a very large string from the database. For timeout testing, set wait_timeout to 5 seconds in your dev environment, add a sleep(10) to your PHP script, and confirm that your reconnection logic successfully catches the error and restores the session.

Related Error Notes