MySQL

MySQL wait_timeout, net_read_timeout & All Timeout Variables (2026 Production Guide)

MySQL wait_timeout, net_read_timeout, innodb_lock_wait_timeout and max_execution_time — production tuning rules and the HikariCP alignment trick that prevents 'communications link failure' errors.

JusDB Team
May 9, 2026
6 min read
10994 views

TL;DR — MySQL timeouts at a glance: The eight timeouts you actually need: wait_timeout (default 28800s — too high for most apps), interactive_timeout (CLI sessions), net_read_timeout / net_write_timeout (TCP read/write window), connect_timeout (handshake), innodb_lock_wait_timeout (deadlock kill, default 50s), max_execution_time (statement-level kill switch, MySQL 5.7+), and the connection-pool side: HikariCP maxLifetime must be lower than MySQL wait_timeout by ≥30s or you'll see 'communications link failure'. Pin each one to your workload, not the defaults.

MySQL's default timeout values were designed for traditional applications where a handful of long-lived connections talk to the database. Modern web applications work completely differently — connection pools opening and closing connections rapidly, microservices with unpredictable request patterns, and serverless functions that spin up connections on demand.

The gap between MySQL's defaults and what modern apps need is where most timeout-related incidents live. "MySQL server has gone away" errors. Connection pool exhaustion at 3am. Transactions holding locks for 8 hours because nobody thought to tune wait_timeout. This guide covers every timeout variable that matters, with production-validated settings for different workload types.

TL;DR — The 5 timeouts that matter most
  • wait_timeout = 300 — kill idle connections after 5 minutes (default is 8 hours — far too long)
  • innodb_lock_wait_timeout = 20 — abort deadlocked transactions faster (default 50s)
  • max_execution_time = 10000 — kill runaway SELECT queries after 10 seconds
  • HikariCP idleTimeout should be 90% of MySQL wait_timeout — misalignment causes "server has gone away"
  • innodb_rollback_on_timeout = ON — fully roll back timed-out transactions (default only rolls back the last statement)

Connection Timeouts

wait_timeout and interactive_timeout

These are the most impactful timeout variables for web applications. They control how long MySQL keeps an idle connection open before closing it.

  • wait_timeout applies to non-interactive connections — application pools, JDBC, mysql2, etc.
  • interactive_timeout applies to interactive clients — the mysql CLI, MySQL Workbench, etc.

The default is 28,800 seconds — 8 hours. For a web app with a connection pool of 50 connections and peak traffic at noon, by 8pm those 50 connections have been sitting idle for hours. Each one is holding a thread on the MySQL server, consuming memory. With enough connections and long enough idle periods, you exhaust MySQL's thread cache and max_connections limit.

-- Production web application
SET GLOBAL wait_timeout = 300;          -- 5 minutes: kills idle app connections
SET GLOBAL interactive_timeout = 1800;  -- 30 minutes: for admin tools

-- Verify they're set
SHOW VARIABLES LIKE '%timeout%';

The HikariCP alignment trap

This causes "MySQL server has gone away" errors for probably 50% of the teams who report it. HikariCP (the most popular Java connection pool) holds connections in the pool indefinitely by default. When MySQL closes a connection after wait_timeout, HikariCP doesn't know — it tries to use the dead connection and gets the error.

The fix: set HikariCP's idleTimeout to 90% of MySQL's wait_timeout. This ensures HikariCP evicts idle connections before MySQL does.

# Spring Boot / HikariCP — align with MySQL wait_timeout=300 (5 minutes)
spring:
  datasource:
    hikari:
      idle-timeout: 270000          # 4.5 minutes (90% of 300s wait_timeout)
      max-lifetime: 1740000         # 29 minutes (stay below MySQL's max)
      connection-timeout: 30000     # 30 seconds to get a connection from the pool
      maximum-pool-size: 20
      minimum-idle: 5
    url: jdbc:mysql://host:3306/db?socketTimeout=30000&connectTimeout=10000

# Node.js (mysql2)
const pool = mysql.createPool({
  waitForConnections: true,
  connectionLimit: 20,
  idleTimeout: 270000,  // 4.5 minutes
});

# Python (SQLAlchemy)
engine = create_engine(
  'mysql+pymysql://...',
  pool_recycle=270,      # recycle connections after 4.5 minutes
  pool_pre_ping=True,    # test connection before use (catches dead connections)
  pool_timeout=30
)

connect_timeout

Controls how long MySQL waits for a client to complete the initial handshake. The default (10 seconds) is fine for most setups. Increase it only if you're behind a slow proxy or in a high-latency network.

SET GLOBAL connect_timeout = 15;  -- increase for high-latency networks
SET GLOBAL connect_timeout = 5;   -- decrease for DoS protection (faster rejection)

Query Execution Timeouts

max_execution_time

max_execution_time applies only to SELECT statements. It does not affect INSERT, UPDATE, DELETE, or DDL. Default is 0 (disabled). This is the variable that prevents a bad query from running for hours and consuming resources.

-- Global limit: kill any SELECT taking longer than 10 seconds
SET GLOBAL max_execution_time = 10000;  -- milliseconds

-- Session override: allow longer queries in analytics sessions
SET SESSION max_execution_time = 120000;  -- 2 minutes for this session

-- Per-query override with optimizer hint:
SELECT /*+ MAX_EXECUTION_TIME(60000) */
  customer_id, SUM(order_value)
FROM orders
WHERE order_date > '2024-01-01'
GROUP BY customer_id;

-- Monitor how often queries are being killed by this setting
SELECT
  variable_name,
  variable_value
FROM performance_schema.global_status
WHERE variable_name IN ('max_execution_time_exceeded', 'max_execution_time_set');

When a query exceeds max_execution_time, the client gets: ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded. This is a clean error — the query is rolled back, no locks are held.


Lock and Transaction Timeouts

innodb_lock_wait_timeout

When one transaction is waiting for a row lock held by another transaction, innodb_lock_wait_timeout controls how long it waits before giving up. Default: 50 seconds.

In high-concurrency OLTP systems, 50 seconds is far too long. A transaction stuck waiting for a lock for 50 seconds has typically already caused a cascading problem — connection pool exhaustion, user-facing timeouts, etc. Reduce this aggressively in transactional workloads.

-- High-concurrency OLTP: fail fast on lock contention
SET GLOBAL innodb_lock_wait_timeout = 10;  -- 10 seconds

-- Analytics workloads: allow complex joins to wait longer
SET GLOBAL innodb_lock_wait_timeout = 120;

-- CRITICAL: enable full rollback on lock timeout
-- Default behavior: only rolls back the last statement, leaving the transaction open!
SET GLOBAL innodb_rollback_on_timeout = ON;

About innodb_rollback_on_timeout: This is one of the most surprising MySQL defaults. When a lock wait times out, MySQL by default only rolls back the last statement of the transaction, not the entire transaction. The transaction stays open, holding whatever locks it already acquired. This can cause lock chains that cascade through your application. Always enable full rollback in production.

lock_wait_timeout

Controls how long DDL operations wait for metadata locks. Unlike innodb_lock_wait_timeout (which handles row locks), this handles table-level metadata locks — relevant when running ALTER TABLE, RENAME TABLE, etc. Default: 31,536,000 seconds (1 year — effectively infinite).

-- Prevent ALTER TABLE from blocking indefinitely waiting for a long transaction
SET GLOBAL lock_wait_timeout = 300;  -- 5 minutes

-- This matters in production: if a long transaction holds a metadata lock,
-- ALTER TABLE will block all subsequent queries on that table until it gets
-- the metadata lock. With a 1-year default, this can halt your app.

Network and I/O Timeouts

-- net_read_timeout: how long MySQL waits to receive data from client
SET GLOBAL net_read_timeout = 30;   -- default 30s, usually fine

-- net_write_timeout: how long MySQL waits to write data to client
SET GLOBAL net_write_timeout = 60;  -- default 60s

-- Increase for large result sets over slow networks:
SET GLOBAL net_write_timeout = 120;

-- For clients reading very slowly (e.g., streaming large exports):
SET SESSION net_write_timeout = 300;

Configurations by Workload Type

High-traffic web application

[mysqld]
# Kill idle connections quickly — prevent pool exhaustion
wait_timeout = 300
interactive_timeout = 1800

# Fast deadlock resolution
innodb_lock_wait_timeout = 10
innodb_rollback_on_timeout = ON
innodb_deadlock_detect = ON

# Kill runaway SELECT queries
max_execution_time = 5000    # 5 seconds

# Connection management
connect_timeout = 10
max_connections = 500
thread_cache_size = 32

Analytics / data warehouse workloads

[mysqld]
# Long-running queries need more time
wait_timeout = 3600
interactive_timeout = 7200
max_execution_time = 300000    # 5 minutes — override per-query with hints

# Allow complex analytics queries to wait for locks
innodb_lock_wait_timeout = 120
innodb_rollback_on_timeout = ON

# More conservative on connections (fewer, longer-lived)
max_connections = 100

OLTP (e-commerce, fintech)

[mysqld]
# Fail fast on lock contention — don't block checkout for 50s
wait_timeout = 180
interactive_timeout = 600
innodb_lock_wait_timeout = 5    # 5 seconds: fail fast, retry at application layer
innodb_rollback_on_timeout = ON
innodb_deadlock_detect = ON

# Strict query execution limit
max_execution_time = 3000    # 3 seconds

# Connection settings
connect_timeout = 10

Monitoring and Troubleshooting

Find long-running transactions

-- Transactions running more than 30 seconds
SELECT
  trx_id,
  trx_state,
  TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS running_sec,
  trx_rows_locked,
  trx_rows_modified,
  trx_query
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 30
ORDER BY running_sec DESC;

Find lock waits

-- Who is blocking whom
SELECT
  r.trx_id AS waiting_trx,
  r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_query,
  b.trx_id AS blocking_trx,
  b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_query,
  TIMESTAMPDIFF(SECOND, r.trx_wait_started, NOW()) AS wait_seconds
FROM information_schema.INNODB_TRX r
JOIN performance_schema.data_lock_waits w ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID
JOIN information_schema.INNODB_TRX b ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
ORDER BY wait_seconds DESC;

Monitor connection churn

-- Connection health metrics
SELECT
  variable_name,
  variable_value
FROM performance_schema.global_status
WHERE variable_name IN (
  'Threads_connected',
  'Threads_running',
  'Max_used_connections',
  'Aborted_connects',        -- connection handshake failures
  'Aborted_clients',         -- clients that disconnected improperly
  'max_execution_time_exceeded'
);

-- Aborted_clients increasing rapidly = wait_timeout too short or app not closing connections
-- Aborted_connects increasing = authentication failures or connect_timeout too short

Current timeout settings at a glance

SHOW VARIABLES WHERE Variable_name IN (
  'wait_timeout',
  'interactive_timeout',
  'connect_timeout',
  'innodb_lock_wait_timeout',
  'lock_wait_timeout',
  'innodb_rollback_on_timeout',
  'max_execution_time',
  'net_read_timeout',
  'net_write_timeout'
);

Diagnosing Common Timeout Errors

"MySQL server has gone away" (error 2006)

Almost always caused by wait_timeout vs connection pool misalignment. Check:

  1. What is your wait_timeout? SHOW VARIABLES LIKE 'wait_timeout'
  2. What is your pool's idleTimeout or pool_recycle?
  3. Is pool_pre_ping / testOnBorrow enabled? (validates connections before use)
  4. Is max_allowed_packet large enough for your queries?

"Lock wait timeout exceeded" (error 1205)

A transaction waited for a row lock longer than innodb_lock_wait_timeout. Check:

  1. Is there a long-running transaction holding the lock? (query INNODB_TRX)
  2. Is innodb_rollback_on_timeout ON? If not, the waiting transaction might still be open
  3. Is innodb_deadlock_detect ON? (deadlocks should resolve faster than lock timeouts)

Queries killed by max_execution_time

Check max_execution_time_exceeded counter. If it's high, you either have runaway queries or the limit is too aggressive for your workload. Review slow query log for queries near the threshold.


Working with JusDB

Timeout misconfigurations are one of the most common sources of production incidents we investigate — and one of the easiest to fix once you know what to look for. The combination of MySQL defaults designed for the 2000s applied to modern connection-pooling architectures creates predictable failure modes.

If you're experiencing "server has gone away" errors, connection exhaustion, or lock wait timeouts, our MySQL consulting team can audit your configuration and fix the root cause. Most of these issues resolve in a single session.

Related reading: MySQL Performance Tuning Guide | MySQL Connection Management | MySQL InnoDB Cluster Guide

Frequently Asked Questions

What is mysql wait_timeout and what should it be set to?
wait_timeout is the seconds MySQL waits on an idle non-interactive connection before closing it. Default: 28800s (8 hours). Production rule: set it to 10–15 minutes (600–900s) for web apps, lower for serverless. Match it to your connection pool's max-idle so the pool refreshes connections before MySQL drops them.
What's the difference between wait_timeout and interactive_timeout?
interactive_timeout applies to clients that connected with CLIENT_INTERACTIVE (typically the mysql CLI). wait_timeout applies to everything else (apps, JDBC, ORMs). On session establishment MySQL copies whichever applies into the session-level wait_timeout. You almost always tune wait_timeout, not interactive_timeout.
What does net_read_timeout do and when does MySQL trigger it?
net_read_timeout is how long MySQL will wait for a packet from the client mid-query. Default 30s. If the network stalls (e.g. client paused mid-bulk-INSERT), MySQL kills the connection after this. Increase to 60–120s for batch loaders or pipelines streaming large payloads.
How should HikariCP maxLifetime be configured against wait_timeout?
Set HikariCP maxLifetime at least 30 seconds lower than MySQL wait_timeout (and any infrastructure timeout — load balancers often kill at 60–350s). Otherwise the pool will hand out a connection MySQL has already closed → 'CommunicationsException: Communications link failure'. Default HikariCP maxLifetime is 1800000ms (30 min).
What is innodb_lock_wait_timeout and how does it differ from wait_timeout?
Different category. innodb_lock_wait_timeout (default 50s) is how long a transaction waits for a row lock before InnoDB kills the lock-acquisition with error 1205. wait_timeout kills idle connections with no active transaction. A blocked transaction holds open the connection — only innodb_lock_wait_timeout stops it.
How do I diagnose 'MySQL server has gone away' errors?
Three causes, in order of likelihood: 1) connection pool returned a connection MySQL had idled out (fix HikariCP maxLifetime), 2) packet exceeded max_allowed_packet (raise on both server and client to 64–256 MB), 3) network device killed an idle TCP session (set OS-level tcp_keepalive_time below the LB timeout).
Can I set max_execution_time per query instead of globally?
Yes. Use the optimizer hint inline: SELECT /*+ MAX_EXECUTION_TIME(5000) */ ... FROM ... (time in milliseconds). Only kills SELECT statements. Server-side global value is max_execution_time; session value can also be set via SET SESSION max_execution_time = 5000.
Why does AWS RDS have different timeout defaults than vanilla MySQL?
AWS RDS overrides via the parameter group. Notable differences: wait_timeout stays at 28800 but the load balancer (NLB/Proxy) typically kills idle connections at 60–350s, which causes silent connection drops your app sees first. Always set HikariCP maxLifetime below the LB timeout, not the MySQL timeout, on RDS.

Share this article

JusDB Team

Official JusDB content team

Deeper Reading

Curated companion guides for readers who want to go deeper on this topic.