from the memory formula to high availability
Redis handles two fundamentally different jobs in Magento 2: write-heavy session storage and read-heavy caching, often on the same server without any clean separation. This article shows how to calculate the memory needs for Redis sessions, configure env.php and redis.conf for separate instances, and use monitoring to catch it early when Redis sizing hits its limits.
Table of Contents
- 1. Context: Redis as session and cache backend
- 2. Calculating memory needs: sessions, TTL and concurrent users
- 3. env.php configuration: separate Redis instances
- 4. redis.conf tuning: maxmemory, eviction and persistence
- 5. High availability: Sentinel vs. Cluster
- 6. Avoiding session locking and race conditions
- 7. Monitoring: memory, eviction rate and early warning signs
- 8. Common mistakes with Redis for session and cache
- 9. Redis configurations compared
- 10. Summary
- 11. FAQ
1. Context: Redis as session and cache backend
Redis handles two fundamentally different jobs in a typical Magento 2 installation, jobs that in practice often run on the same server despite producing completely different access patterns. As session storage, Redis stores a small but frequently read and written record per visitor: cart reference, customer data flags, CSRF token and form keys. As a cache backend via Cm_Cache_Backend_Redis, the same process instead stores noticeably larger blocks that are written less often but read intensively: compiled layout data, block output, and, with full page cache enabled, entire rendered HTML pages.
The difference shows up most clearly in write load. A session gets updated on practically every request, because Magento rewrites the access timestamp and often CSRF form keys as well. The cache backend, on the other hand, is only rewritten on invalidation, for example after a product change, and otherwise almost exclusively read. Anyone who treats Redis as a single homogeneous instance overlooks the fact that session traffic is latency-critical and write-heavy, while cache traffic is throughput-oriented and read-heavy.
This distinction is not an academic nicety, it directly determines sizing, the maxmemory-policy and the persistence strategy. A Redis instance configured jointly for sessions and cache tends to evict the wrong keys when in doubt: if a session gets evicted under memory pressure, a customer loses their cart or gets logged out unintentionally. If a cache entry gets evicted instead, it only costs a rebuild. The following sections show how to size and separate the instances cleanly for both roles.
2. Calculating memory needs: sessions, TTL and concurrent users
Capacity planning for Redis as a session backend starts with three figures: the average size of a serialized session, the session TTL, and the number of concurrently active sessions during peak load. An average Magento session takes up between 4 and 15 KB depending on the number of cart items, enabled modules and stored form keys. Anyone who estimates this size instead of measuring it either sizes far too generously or runs into production problems during unexpected load spikes such as sale campaigns.
The second factor is the TTL, controlled via max_lifetime in the session configuration. A longer TTL means more sessions stored concurrently, particularly for B2B shops with long decision cycles. The simplified capacity formula is: required memory equals concurrent sessions times average session size times a fragmentation factor of roughly 1.3, to account for internal memory management and a growth buffer. At 50,000 concurrent sessions with an average of 8 KB, that comes out to roughly 520 MB of pure data storage, in practice you should size for at least 750 MB to 1 GB.
Instead of planning with estimates, the following redis-cli commands provide real measurements for your own instance:
# Get the number of keys in the session database
redis-cli -n 2 DBSIZE
# Sample: check memory usage of individual session keys
redis-cli -n 2 --scan --pattern 'sess_*' | head -20 | while read -r key; do
redis-cli -n 2 MEMORY USAGE "$key"
done
# Show total memory usage of the session instance
redis-cli -n 2 INFO memory | grep used_memory_human
# Capacity formula:
# required_RAM_KB = concurrent_sessions * average_size_KB * fragmentation_factor
# Example: 50,000 concurrent sessions, 8 KB average, factor 1.3
echo "$((50000 * 8 * 13 / 10)) KB estimated memory requirement"
DBSIZE returns the number of keys in the selected database, while MEMORY USAGE returns the actual memory consumption of a single key including internal overhead. Anyone who averages these values across multiple samples and multiplies them by the number of active sessions from the access log gets a solid capacity plan instead of a gut-feeling estimate.
3. env.php configuration: separate Redis instances for session, cache and page cache
Magento allows separate connection parameters for cache, page cache and session to be stored in env.php. Ideally this separation should not just happen through different database numbers within the same Redis instance, but through actually separate processes with their own port and their own maxmemory limit. The reason: a shared maxmemory limit for session and cache means a cache surge after a deploy puts session data under eviction pressure, and vice versa.
In practice, a setup with three instances has proven effective: one for the default cache and configuration cache, one for the full page cache with typically much larger values, and a dedicated instance solely for sessions. Each instance gets its own port, its own maxmemory, and an eviction policy tuned to its access pattern.
// app/etc/env.php - excerpt: separate Redis instances for cache, page_cache and session
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => '127.0.0.1',
'port' => '6379',
'database' => '0',
'compress_data' => '1',
'compression_lib' => 'gzip',
],
],
'page_cache' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => '127.0.0.1',
'port' => '6380',
'database' => '0',
'compress_data' => '0',
],
],
],
],
'session' => [
'save' => 'redis',
'redis' => [
'host' => '127.0.0.1',
'port' => '6381',
'database' => '0',
'compression_threshold' => '2048',
'compression_library' => 'gzip',
],
],
compress_data and compression_lib reduce memory needs in the cache backend, but cost CPU cycles on every read and write. For the session instance, compression_threshold makes more sense than blanket compression, because small sessions barely benefit from compression anyway, while every compression step adds latency that flows directly into time-to-first-byte on session access.
4. redis.conf tuning: maxmemory, eviction policy and persistence compared
The maxmemory-policy decides which keys Redis removes once the maxmemory limit is reached. For the cache instance, allkeys-lru is usually the right choice, because practically all stored keys are equivalent cache entries and the least recently used entry can be evicted without risk, a cache rebuild only costs some CPU time. For the session instance, volatile-lru is preferable, because Magento assigns every session a TTL, which means sessions close to expiry are preferentially removed instead of actively used sessions being evicted arbitrarily.
The second key decision concerns persistence. RDB snapshots create a complete memory image at configurable intervals, which for large data volumes causes brief latency spikes due to the fork system call. AOF, on the other hand, logs every write command and enables much more fine-grained recovery, but costs continuously more I/O. For a pure cache instance whose content can be rebuilt from the database at any time, persistence is usually entirely dispensable, save "" fully disables RDB snapshots and saves the associated latency spikes.
For the session instance, the trade-off looks different. Without any persistence, a restart, for example after a server patch, immediately loses all active sessions and thus causes mass unwanted logouts and empty carts. AOF with appendfsync everysec is usually the better compromise here: in the worst case, at most one second of session writes is lost, while continuous I/O load stays limited thanks to batch synchronization.
# redis-cache.conf - dedicated cache and page_cache instance (port 6379/6380)
maxmemory 2gb
maxmemory-policy allkeys-lru
save ""
appendonly no
tcp-keepalive 300
timeout 0
# redis-session.conf - dedicated session instance (port 6381)
maxmemory 1gb
maxmemory-policy volatile-lru
save ""
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
tcp-keepalive 300
timeout 0
5. High availability: Redis Sentinel vs. Redis Cluster for Magento
Once a single Redis process becomes a single point of failure, the question of Redis Sentinel or Redis Cluster comes up. Sentinel monitors a group of Redis instances in a master-replica setup, automatically detects a master failure, and performs a failover to a replica. For Magento this is the more practical solution, because both the session handler module and Cm_Cache_Backend_Redis support Sentinel connections, and Magento simply gets the Sentinel endpoints instead of a single host configured.
Redis Cluster, by contrast, distributes data across multiple shards using hash slot partitioning and scales horizontally beyond the memory of a single server. That sounds attractive at first for very large installations, but Cm_Cache_Backend_Redis, which Magento uses for the cache backend, has no reliable native cluster slotting support for multi-key operations such as tag-based invalidation of cache entries. In practice this means: Cluster for the Magento cache backend brings more complexity than benefit, as long as a single well-sized instance or a Sentinel setup is sufficient.
For very large multi-store installations with several hundred gigabytes of cache data, splitting across multiple independent instances per website or store group, instead of a true cluster, is usually the more robust and easier to operate solution. The decision between Sentinel and Cluster should therefore depend less on raw data volume and more on actual support in Magento's Redis adapters.
6. Avoiding session locking and race conditions
When a browser fires multiple parallel requests for the same user, for example through AJAX calls for cart widgets or simultaneously loaded blocks, several PHP processes access the same session at once. Without locking, the process that writes last overwrites the changes of the others, a classic race condition that can cause items to vanish from the cart. Magento's Redis session handler implements its own locking mechanism on top of the native Redis primitives for this.
The key parameters here are min_lock_lifetime, max_lock_lifetime, max_concurrent_locks and break_after_frontend. min_lock_lifetime prevents a lock from being released again immediately after being set, while max_lock_lifetime defines an upper limit after which a lock is forcibly considered expired, to avoid orphaned locks from crashed processes. break_after_frontend specifies after how many seconds a waiting frontend request gives up waiting for a lock and proceeds without one instead, to prevent long load times caused by requests waiting on each other.
disable_locking turns off the entire locking mechanism and should practically never be set globally, because doing so reintroduces exactly the race conditions locking is meant to prevent. disable_locking makes sense at most for specific, clearly isolated AJAX endpoints that themselves perform no writing session access, for example pure read access for a tracking script.
// app/etc/env.php - session locking configuration against race conditions
'session' => [
'save' => 'redis',
'redis' => [
'host' => '127.0.0.1',
'port' => '6381',
'database' => '0',
'max_lifetime' => '2592000',
'min_lifetime' => '60',
'disable_locking' => '0',
'min_lock_lifetime' => '10',
'max_lock_lifetime' => '35',
'max_concurrent_locks' => '6',
'break_after_frontend' => '5',
'break_after_adminhtml' => '30',
'first_lifetime' => '600',
'bot_first_lifetime' => '60',
'bot_lifetime' => '7200',
'log_level' => '1',
],
],
7. Monitoring: memory usage, eviction rate and early warning signs
Redis provides detailed operational metrics via the INFO command that go far beyond raw memory usage. The memory section provides used_memory_human for current consumption and mem_fragmentation_ratio, a value noticeably above 1.5 indicates memory fragmentation caused by frequent key size changes, which can be addressed with the activedefrag defragmentation feature.
The most important early warning metric is evicted_keys from the stats section. If this counter keeps rising, the system is actively evicting keys because maxmemory has been reached, which leads to increased cache miss rates on the cache instance and, in the worst case, lost user sessions on the session instance. In addition, the ratio of keyspace_hits to keyspace_misses shows how effectively the cache is actually working, a declining hit rate is often the first visible symptom of a maxmemory that has been sized too tightly.
# Live stats: operations per second, memory, client connections
redis-cli -p 6379 --stat
# Memory information including fragmentation
redis-cli -p 6379 INFO memory | grep -E 'used_memory_human|mem_fragmentation_ratio|maxmemory_human'
# Eviction and hit rate as early warning signal
redis-cli -p 6379 INFO stats | grep -E 'evicted_keys|expired_keys|keyspace_hits|keyspace_misses'
# Slowlog for commands above a threshold (microseconds)
redis-cli -p 6379 CONFIG SET slowlog-log-slower-than 10000
redis-cli -p 6379 SLOWLOG GET 10
For ongoing monitoring, redis-cli --stat is useful, continuously printing metrics in one line per interval, ideal for quick diagnosis directly on the server. For production monitoring, however, these metrics should additionally be exported to a monitoring system like Prometheus using redis_exporter, so threshold alerts trigger automatically before users notice the effects of undersized instances in the frontend.
8. Common mistakes with Redis for session and cache
The most common mistake is a shared Redis database for session and cache without separating maxmemory and eviction policy. As soon as a large cache rebuild after a deploy hits the memory limit of the shared instance, the system evicts session keys as well depending on policy, causing a wave of unwanted logouts right after every deployment, a pattern that in production logs is often only recognized as a capacity problem after weeks, because the symptoms initially look like an application bug.
A second common mistake is a missing or poorly chosen persistence strategy. If persistence is skipped entirely for the session instance because it supposedly saves performance, every planned or unplanned restart causes a complete loss of sessions. Conversely, an overly aggressive RDB snapshot configuration with short intervals on a heavily writing session instance costs noticeable latency spikes from the fork call, particularly with large amounts of data in memory.
A third mistake concerns the timeout handling of the Redis connection itself. A connect_timeout or read_timeout set too low in env.php causes frequent but avoidable errors during brief network spikes, while a timeout set too high means a genuinely failed server blocks PHP processes for minutes instead of quickly returning an error. A timeout in the range of 2.5 to 5 seconds with retry logic enabled is a sensible starting point for most setups.
9. Redis configurations for different shop sizes compared
How much RAM, which eviction policy and which persistence strategy make sense for Redis depends heavily on shop size and traffic pattern. The table below shows rough benchmark values for typical constellations, separated for session and cache instances respectively.
| Shop size | RAM recommendation | maxmemory-policy | Persistence |
|---|---|---|---|
| Small (up to 500 concurrent sessions) | Session 256 MB, cache 512 MB | volatile-lru / allkeys-lru | RDB and AOF disabled |
| Medium (500 to 5,000) | Session 1 GB, cache 2 GB | volatile-lru / allkeys-lru | AOF everysec (session), RDB off (cache) |
| Large (5,000 to 20,000) | Session 4 GB, cache 8 GB | volatile-lru / allkeys-lru | AOF everysec (session), RDB off (cache) |
| Enterprise multi-store (20,000+) | Session 8 to 16 GB, cache 16 to 32 GB | volatile-lru / allkeys-lfu | AOF everysec plus replication, RDB hourly (cache) |
| B2B with long sessions | Session 4 to 8 GB, cache 4 to 8 GB | volatile-ttl / allkeys-lru | AOF everysec (session), RDB every 6h (cache) |
These values are starting points for your own capacity planning, not fixed rules. Anyone who regularly runs the redis-cli commands shown in section 2 against their own installation can replace the table values with real measurements within a few weeks and adjust the instances accordingly.
10. Summary
The key takeaway around Redis as session and cache backend in Magento 2 is: both roles need separate instances, separate maxmemory limits and separate eviction policies. The memory requirement for sessions can be calculated from average session size, TTL and concurrent users instead of being estimated. env.php and redis.conf should define separate ports for cache, page cache and session, plus a persistence strategy tuned to the access pattern, allkeys-lru without persistence for the cache, volatile-lru with AOF everysec for sessions.
Session locking with sensibly chosen min_lock_lifetime and break_after_frontend values prevents race conditions on parallel requests, while Redis Sentinel is the more practical high availability solution for most Magento installations compared to Cluster. Continuous monitoring of evicted_keys, mem_fragmentation_ratio and hit rate makes undersized instances visible before customers experience them in the frontend as a vanished cart or an unwanted logout.
Redis as session and cache backend: the essentials at a glance
Memory requirement
Concurrent sessions times average size times fragmentation factor 1.3. Measure with redis-cli MEMORY USAGE and DBSIZE instead of estimating.
env.php separation
Its own Redis instance with its own port for cache, page cache and session, instead of just different database numbers on one instance.
redis.conf tuning
allkeys-lru without persistence for cache, volatile-lru with AOF everysec for session. Different access patterns need different policies.
Monitoring
evicted_keys as an early warning signal, check mem_fragmentation_ratio above 1.5, redis-cli --stat for quick live diagnosis.
11. FAQ: Redis as Session and Cache Backend in Magento 2
1Why separate session storage from the cache backend?
2Calculating the memory requirement for sessions?
3Which maxmemory-policy for sessions?
4Does the cache instance need persistence?
5What does disable_locking do?
6Sentinel or Cluster for Magento?
7Which metrics warn early?
8What happens with a shared database?
9RAM for a mid-sized shop?
10AOF or RDB for sessions?
Mironsoft
Magento 2 performance, Redis architecture and scaling
Redis sized correctly instead of configured at random?
We analyze your existing Redis configuration, calculate the real memory requirement for session and cache, and cleanly separate the instances, including a Sentinel setup for high availability.
Capacity analysis
Measure memory needs for session and cache instead of estimating
Instance separation
Separate env.php and redis.conf for session, cache and page cache
High availability
Set up Redis Sentinel and build monitoring with early warning signals