Your Magento 2 store runs fine at 2 AM. Then a promo email goes out, traffic triples, and the logs start bleeding this:

report.CRITICAL: Error: read error on connection to 127.0.0.1:6379 in /var/www/vendor/colinmollenhour/credis/Client.php:1044
Exception: Warning: Redis::write(): send of 12 bytes failed with errno=32 Broken pipe

Checkout hangs. Add-to-cart throws 503s. And the frustrating part: Redis itself looks healthy in redis-cli info. This is almost never a "Redis is down" problem. It's a connection lifecycle and timeout problem that only surfaces when concurrency climbs.

Let's walk through exactly what breaks and how to lock it down at the Magento, PHP, and Redis layers.

Quick Diagnostic Cheat-Sheet

SymptomRoot CauseImmediate Diagnostic Command
"read error on connection" in exception.logRedis timeout closing idle sockets mid-requestredis-cli config get timeout
"Broken pipe" / errno=32 under loadmaxclients hit; Redis rejecting new connectionsredis-cli info clients
Slow checkout, session locksSession lock wait exceeding PHP execution timeredis-cli -n 2 keys '*_lock' | wc -l
OOM errors, evicted keysmaxmemory reached, wrong eviction policyredis-cli info memory | grep evicted
Random cache misses under spikesCache DB and session DB sharing one instanceredis-cli info keyspace

Step 1: Confirm It's a Timeout, Not an Outage

Before touching config, prove Redis is actually alive and the errors are transient. Watch the connection count live while you replay the traffic pattern:

redis-cli info clients
# connected_clients:812
# blocked_clients:0

redis-cli info stats | grep rejected_connections
# rejected_connections:1439

A non-zero rejected_connections that climbs during spikes is your smoking gun. Redis is refusing sockets because it hit maxclients, or it's slamming idle connections shut because of an aggressive timeout value.

Do not restart Redis during an active spike to "fix" it. Flushing the object cache under load forces every PHP worker to regenerate blocks and full-page cache simultaneously, which will spike CPU and make the outage worse, not better.

Step 2: Disable the Idle Connection Timeout

The single most common cause of this exact error is a Redis timeout setting greater than zero. Magento holds persistent connections open across a request. If Redis closes an idle socket after N seconds, the next command Magento sends hits a dead pipe.

Check it:

redis-cli config get timeout
# 1) "timeout"
# 2) "300"

Set it to 0 (never close idle connections) and persist it in redis.conf:

# /etc/redis.conf
timeout 0
tcp-keepalive 60

Apply live without a full restart:

redis-cli config set timeout 0
redis-cli config rewrite

tcp-keepalive 60 tells Redis to send keepalive probes so genuinely dead peers still get cleaned up without killing healthy Magento connections.

Step 3: Raise maxclients and Backlog

Under real concurrency, LiteSpeed PHP workers each open their own Redis sockets. Multiply your PHP worker count across cache, session, and FPC connections and the default 10000 can still be starved by ulimit. Check the effective limit:

redis-cli config get maxclients
# often reports lower than configured due to ulimit -n

Raise the OS file descriptor limit for the Redis service and the ceiling in config:

# /etc/systemd/system/redis.service.d/limits.conf
[Service]
LimitNOFILE=65535
# /etc/redis.conf
maxclients 20000
tcp-backlog 511
systemctl daemon-reload
systemctl restart redis

Step 4: Split Cache and Sessions Onto Separate Databases

Running the object cache, full-page cache, and sessions on one Redis DB means a session-heavy checkout flow competes with FPC reads. Separate them in app/etc/env.php so eviction and memory pressure on one never starves the other:

'cache' => [
    'frontend' => [
        'default' => [
            'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
            'backend_options' => [
                'server' => '127.0.0.1',
                'port' => '6379',
                'database' => '0',
                'compress_data' => '1',
                'connect_retries' => '3',
                'read_timeout' => '10',
            ],
        ],
        'page_cache' => [
            'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
            'backend_options' => [
                'server' => '127.0.0.1',
                'port' => '6379',
                'database' => '1',
                'compress_data' => '0',
            ],
        ],
    ],
],
'session' => [
    'save' => 'redis',
    'redis' => [
        'host' => '127.0.0.1',
        'port' => '6379',
        'database' => '2',
        'timeout' => '2.5',
        'disable_locking' => '0',
        'max_concurrency' => '20',
        'break_after_frontend' => '5',
        'bot_first_lifetime' => '60',
        'bot_lifetime' => '7200',
    ],
],

Note read_timeout => 10 on the cache backend. If PHP's socket read timeout is lower than a slow Redis command, you get the read error even when Redis eventually responds.

Step 5: Fix Session Lock Contention

Magento uses Redis session locking to prevent concurrent writes. Under load, a slow AJAX-heavy theme fires multiple parallel requests per user, each waiting on the session lock. When the wait exceeds PHP's max_execution_time, the connection dies mid-lock.

Tune break_after_frontend and max_concurrency (shown above) and confirm PHP can outlast the lock wait:

# php.ini or CloudLinux per-user override
max_execution_time = 60
default_socket_timeout = 60
redis.session.locking_enabled = 1
On shared plans, CloudLinux LVE limits cap the number of concurrent PHP processes per account. If you keep hitting session lock waits despite tuning, you've outgrown a shared PHP worker pool and need dedicated resources on a Cloud VPS.

Step 6: Set Memory Ceiling and Eviction Policy

An unbounded Redis will eventually swap or get OOM-killed, dropping every connection at once. Set a hard ceiling and use allkeys-lru for cache DBs so Magento cache eviction is graceful:

# /etc/redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lru

Verify nothing is silently evicting sessions, which would log users out mid-checkout:

redis-cli -n 2 info stats | grep evicted_keys
# evicted_keys:0  <-- good, sessions are not being purged

Step 7: Flush, Warm, and Load Test

After config changes, flush the Magento cache cleanly and warm the full-page cache before real traffic returns:

php bin/magento cache:flush
php bin/magento cache:enable

# Warm FPC with your sitemap
wget --quiet -O - https://yourstore.com/sitemap.xml \
  | grep -oP '(?<=<loc>)[^<]+' \
  | xargs -P 4 -n 1 curl -s -o /dev/null

Then replay the load and watch rejected_connections stay flat at zero.

Frequently Asked Questions

Why does Redis work fine at low traffic but fail during spikes?

At low concurrency, few connections stay open long enough to hit the idle timeout or exhaust maxclients. Spikes multiply persistent connections across every PHP worker, exposing timeout and file-descriptor limits that never triggered before.

Should I use one Redis instance or separate ones for cache and sessions?

A single instance with separate databases (0, 1, 2) is fine for most stores and keeps management simple. For very high-volume stores, run a second Redis instance for sessions so a cache flush or FPC eviction storm never touches active user sessions.

Is setting Redis timeout to 0 safe?

Yes, for Magento. Combined with tcp-keepalive 60, Redis still detects and reaps genuinely dead peers via keepalive probes, but it stops killing healthy idle connections that Magento reuses across requests.

Preventing the Bottleneck at the Infrastructure Layer

Most of these Redis failures trace back to a platform that wasn't provisioned for Magento's connection appetite: too few file descriptors, throttled PHP workers, and slow disk-backed cache fallbacks. Hostiso runs Magento on LiteSpeed with tuned Redis object caching and NVMe storage, so cache reads stay in memory and connection pools don't collapse when a promo hits. If your store is bursting past its current resource envelope, a Cloud VPS gives you dedicated PHP workers and a Redis instance you fully control, while heavy database-driven catalogs run best on our Dedicated Servers. Tune the config, size the hardware to the traffic, and the read errors disappear for good.