Using Redis as a Caching Layer in Magento Correctly
AI generated
60fps
ms
Performance · Redis · Caching · Magento 2
Using Redis as a Caching Layer in Magento Correctly
Cleanly separating sessions, object cache, and Full Page Cache

Redis handles three distinct roles in Magento at once: session storage, object cache backend, and optionally Full Page Cache. Storing all three roles in the same database risks tag collisions, unnecessary cache flushes, and in the worst case logged-out customers from lost sessions. This article shows how to configure, monitor, and protect Redis in Magento from running out of memory.

14 min. read Redis 7.x · env.php · maxmemory-policy Magento 2.4.8 · Cache Tags · RDB/AOF

1. Why Redis handles three roles in Magento

In a Magento setup, Redis does not play one role, it plays three at once: session storage, cache backend for the object cache, and optionally a Full Page Cache backend for page_cache. Without Redis, the object cache runs by default through Cm_Cache_Backend_File and writes thousands of small files to disk, which creates inconsistencies across multiple application servers because each server keeps its own local cache. Redis solves this by letting all application servers share the same central cache store, so invalidations become visible to every server immediately.

The three roles are configured separately in env.php: the session block, the cache block with the frontend/default sub-key for the object cache, and optionally cache/frontend/page_cache for the Full Page Cache. Technically, all three can share the same Redis process, but they should use different logical databases via the database index. This separation is not cosmetic, it prevents collisions in tag-based invalidation and makes targeted FLUSHDB operations possible without wiping out active customer sessions.

2. Configuring Redis for session storage

Session configuration lives in env.php under session, with save set to redis. Besides host, port, and database, two parameters matter most: session locking and bot_first_lifetime. Magento uses session locking by default so that parallel requests to the same session, such as AJAX cart updates, do not overwrite each other. The parameters bot_first_lifetime and bot_lifetime reduce the wait time for bot traffic, which never produces write conflicts anyway, preventing unnecessary lock spinning from crawlers.

The disable_locking parameter should never be set to 1 in production unless the application is provably free of concurrent writes to the same session, otherwise race conditions appear on the cart. The TTL of session keys in Redis is derived from session.gc_maxlifetime in php.ini, not from a dedicated Magento setting. The big advantage over filesystem sessions: multiple application servers share the same session store, so a load balancer can run without sticky sessions and a server outage no longer logs users out.


<?php
// app/etc/env.php excerpt: separate Redis roles with distinct database indices
return [
    'session' => [
        'save' => 'redis',
        'redis' => [
            'host' => '127.0.0.1',
            'port' => '6379',
            'database' => '0',
            'password' => '',
            'timeout' => '2.5',
            'persistent_identifier' => 'magento_session',
            'bot_first_lifetime' => '60',
            'bot_lifetime' => '7200',
            'max_lifetime' => '2592000',
            'min_lifetime' => '60',
            'disable_locking' => '0',
            'log_level' => '1',
        ],
    ],
    'cache' => [
        'frontend' => [
            'default' => [
                'backend' => 'Magento\Framework\Cache\Backend\RemoteSynchronizedCache',
                'backend_options' => [
                    'server' => '127.0.0.1',
                    'port' => '6379',
                    'database' => '1',
                    'compress_data' => '1',
                    'compression_lib' => 'gzip',
                    'persistent' => 'magento_cache',
                ],
            ],
            'page_cache' => [
                'backend' => 'Magento\Framework\Cache\Backend\Redis',
                'backend_options' => [
                    'server' => '127.0.0.1',
                    'port' => '6379',
                    'database' => '2',
                    'compress_data' => '1',
                    'compression_lib' => 'gzip',
                    'persistent' => 'magento_page_cache',
                ],
            ],
        ],
    ],
];

3. Redis as the cache backend for object cache

The object cache is routed to a Redis backend type through cache/frontend/default/backend_options. What matters is a dedicated database index, different from session and page_cache, database 1 instead of 0 in the example above. The compress_data parameter enables compression for cached values and should be set to 1: layout caches and configuration caches contain a lot of redundant, highly compressible text, so memory usage can drop by 50 to 70 percent depending on catalog size. compression_lib controls the library, gzip is the stable default, lz4 or zstd are faster at a slightly higher CPU cost.

For connection handling, persistent is the decisive parameter: without it, every PHP-FPM worker opens a new TCP connection to Redis on every request, handshake overhead included. With a persistent ID set, the connection is reused across requests, which saves noticeable latency under load. The phpredis extension handles the actual connection pooling at the PHP process level; Credis is only a fallback when phpredis is not installed.

4. Redis as a Full Page Cache backend

There are two fundamentally different architectures for the Full Page Cache: Varnish as a reverse proxy in front of the web server, or Redis as the built-in page_cache backend that Magento serves itself through PHP. Varnish answers cache hits without a PHP process ever starting, and it supports Edge Side Includes, which let personalized blocks like the mini cart or login status be carved out of the cached HTML and loaded separately. That makes Varnish practically unmatched under high traffic.

Redis as an FPC backend is simpler to operate because there is no separate reverse proxy sitting in front of the application and no extra SSL termination to configure. The downside: every request still passes through Magento's PHP bootstrap process before the cached HTML response is loaded from Redis and delivered, which is noticeably slower than Varnish's zero-PHP response path. For smaller and mid-sized stores without dedicated infrastructure access to Varnish, for staging environments, and for setups behind managed hosting proxies without Varnish support, Redis FPC is still the more pragmatic choice.

5. Understanding cache tag invalidation

Every cacheable entity in Magento is tagged when it is stored, for example cat_p_123 for a product or cat_c_45 for a category. When a product changes, Magento internally triggers invalidation for exactly those tags, not for the entire cache. The command bin/magento cache:clean clears targeted, invalidated cache types via the TypeListInterface, while bin/magento cache:flush wipes the entire Redis keyspace of that role via FLUSHDB, including every still-valid entry.

Magento's Redis cache backend implements tag mapping through extra index keys in the format zc:ta:<tag>, which reference the associated cache keys as sets. When a tag is cleared, Redis iterates over this set and removes each referenced key individually, which takes noticeably longer for very large tag groups, such as a global price update, than a simple FLUSHDB. In practice this means: frequent, small tag invalidations are cheaper than rare, large batch invalidations across thousands of products at once.


<?php
declare(strict_types=1);

namespace Mironsoft\CacheTools\Model;

use Magento\Framework\App\CacheInterface;

/**
 * Cleans cache entries selectively by tag instead of flushing the whole store.
 */
class TagInvalidator
{
    public function __construct(
        private readonly CacheInterface $cache
    ) {
    }

    /**
     * Invalidate only the cache entries tagged with the given product IDs.
     *
     * @param int[] $productIds
     * @return void
     */
    public function invalidateProducts(array $productIds): void
    {
        $tags = array_map(static fn (int $id): string => 'cat_p_' . $id, $productIds);

        // Selective clean: only keys referenced by these tags are removed,
        // the rest of the object cache stays warm.
        $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $tags);
    }
}

6. Persistence strategies: RDB vs. AOF

RDB snapshots write the entire dataset to disk as a binary file at configurable intervals, for example save 900 1, save 300 10, save 60 10000. The advantage: compact files, fast restarts, low runtime overhead between snapshots. The downside: every write since the last snapshot is lost on a crash. For pure cache roles like object cache and Full Page Cache, that is harmless, a cache miss after a crash is just a recomputation, not lost data.

The session role needs a different assessment: lost sessions mean an involuntary logout for hundreds of concurrently active customers and, in the worst case, a lost cart. Here, append-only-file persistence with appendonly yes and appendfsync everysec pays off as a compromise between data safety and I/O load. appendfsync always syncs on every write command and is noticeably slower as a session store, without the extra safety margin justifying the cost in production. For cache and FPC roles, persistence is often entirely dispensable.


# redis.conf: durable persistence for the session role (database 0)
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
save 900 1
save 300 10

# redis.conf: cache and FPC roles do not need durability -
# a cache miss just costs one recomputation, not customer data
# (apply via a second instance or CONFIG SET per logical database use case)
save ""
appendonly no

7. Choosing the right memory eviction policy

maxmemory defines the hard memory ceiling per Redis instance, maxmemory-policy determines what happens once that ceiling is hit. The default setting, noeviction, rejects new write commands with an error as soon as memory is full. For a Magento cache, that means exceptions on every page load once the limit is reached, in the worst case, instead of quiet, unnoticeable behavior.

For object cache and Full Page Cache, allkeys-lru is the right choice: under memory pressure, Redis removes the least recently used keys regardless of whether a TTL is set. For the session store, volatile-lru is preferable, because it only removes keys with a TTL set, which is consistently the case for Magento sessions. noeviction for the session store is also defensible, as long as maxmemory is generously sized and actively monitored. The evicted_keys counter in INFO stats immediately shows whether a policy is actually kicking in in practice, or whether the memory ceiling is simply too tight to begin with.

8. Monitoring the Redis hit rate

redis-cli INFO stats returns the two central counters keyspace_hits and keyspace_misses. The hit rate is calculated as hits / (hits + misses) and should stay well above 90 percent for the object cache during normal operation, since configuration and layout rarely change. Short-lived dips right after a deployment or a reindex are normal, because tags get invalidated deliberately at that point and the cache has to rebuild.

For ongoing observation, redis-cli --stat works well, printing connections, memory usage, hits, and evictions as a live table every second. redis-cli --bigkeys scans the entire keyspace and identifies unusually large individual keys, a common sign of missing compression or poorly sized cache entries. For production monitoring over time, a dedicated redis_exporter for Prometheus with a Grafana dashboard is recommended, making hit rate, memory usage, and eviction rate traceable historically and triggering automatic alerts when thresholds are crossed.


# Hit rate from raw counters
redis-cli -n 1 INFO stats | grep -E 'keyspace_(hits|misses)|evicted_keys'
# keyspace_hits:8452110
# keyspace_misses:214332
# evicted_keys:0

# Live overview: connections, memory, ops/sec, hits, evictions
redis-cli --stat

# Find oversized keys, common sign of missing compression
redis-cli -n 1 --bigkeys

# Current memory usage vs the configured hard limit
redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'

9. Common Magento Redis misconfigurations

The most common mistake is sharing the same Redis database across session, object cache, and Full Page Cache. A FLUSHDB for the cache then accidentally deletes every active session too, and a mass tag invalidation for products can, in rare cases, collide with session keys if prefixes are not cleanly separated. The fix is always the same: three different database indices, ideally even three separate Redis instances or at least separate maxmemory limits per logical database.

The second most common mistake is a missing or wrong maxmemory-policy. Without explicit configuration, Redis stays at noeviction, which leads to rejected write commands and exceptions in the store once memory is full, instead of gracefully evicting cache entries. Missing compression, when compress_data is not set, wastes significant memory on large catalogs and thereby accelerates exactly the OOM risk that a correct eviction policy is supposed to cushion. Missing persistent connections create unnecessary TCP handshake overhead on every request, and a missing persistent identifier across multiple Magento instances sharing one Redis leads to cache collisions between staging and production.


# Quick diagnostic pass before deploying a new Magento environment

# 1. Check which eviction policy is actually active
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET maxmemory

# 2. Verify roles are not sharing the same logical database
for i in 0 1 2; do
  echo "db$i: $(redis-cli -n $i DBSIZE)"
done

# 3. Benchmark connection overhead with and without persistent connections
redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -q -t GET,SET

# 4. docker-compose.yaml: one Redis service, three isolated logical roles
#    (or split into three services for full isolation under heavy load)
services:
  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru
    ports:
      - "6379:6379"
Redis role DB index Wrong configuration Correct configuration
Sessions 0 allkeys-lru + shared DB with cache volatile-lru, own DB, AOF everysec
Object cache 1 noeviction without monitoring allkeys-lru, compress_data enabled
Full Page Cache 2 same DB as object cache allkeys-lru, own DB, save disabled
Persistence choice - appendfsync always everywhere everysec as the default compromise
Connection handling - no persistent parameter set persistent ID reduces handshake overhead

In practice, the three Redis roles are closely linked: a poorly sized eviction policy in the object cache increases load on the database, because entries have to be recomputed more often, which in turn affects response times and indirectly the Full Page Cache hit rate as well. Consistently applying the separation from the table and keeping an eye on the metrics from section 8 prevents most production-relevant Redis outages before they ever happen.

Mironsoft

Redis architecture, cache tuning, and performance audits for Magento stores

Redis caching that will not blow up on you?

We analyze your existing Redis configuration, cleanly separate session, cache, and FPC roles, and set up eviction policies, persistence, and monitoring so your store stays stable even under load.

Redis audit

Reviewing and fixing DB separation, eviction policy, and persistence strategy

Cache tuning

Optimizing compression, connection pooling, and tag invalidation

Monitoring setup

Hit rate tracking with Prometheus/Grafana and automatic alerts

10. Summary

Redis as a caching layer in Magento addresses one core problem: session storage, object cache, and Full Page Cache each have different requirements around persistence, eviction, and availability, yet they often share the same Redis instance. Separate database indices, a maxmemory-policy that matches the role, and a deliberate choice between RDB, AOF, or no persistence at all prevent the most common production outages. Compression via compress_data and persistent connections additionally cut memory usage and latency noticeably, without requiring any extra infrastructure.

The decisive difference between a stable and a fragile Redis setup rarely comes down to the choice of tool itself, but to consistently separating the three roles and continuously monitoring hit rate, memory usage, and eviction rate. Regularly reviewing INFO stats and proactively watching thresholds, rather than reacting only after the first OOM crash, keeps Redis reliably in check as a fast caching layer over the long run.

Redis as a Caching Layer in Magento - The Essentials at a Glance

Separate the three roles

Session, object cache, and Full Page Cache need their own database indices, ideally their own Redis instances.

Eviction policy per role

allkeys-lru for pure caches, volatile-lru for sessions, never noeviction without monitoring.

Choose persistence deliberately

AOF everysec for sessions, RDB or no persistence at all for pure cache roles.

Monitor the hit rate

Review INFO stats regularly, treat evicted_keys as an early warning for an undersized maxmemory.

11. FAQ: Redis as a Caching Layer in Magento

1Why does Magento need Redis for sessions, cache, and Full Page Cache at the same time?
Central session storage for multiple application servers, a fast object cache backend instead of a file-based cache, and optionally Full Page Cache for rendered HTML output. All three roles should be configured separately.
2What is the difference between cache:clean and cache:flush?
cache:clean invalidates specific types via the TypeListInterface, cache:flush wipes the entire Redis keyspace of that role via FLUSHDB, including still-valid entries.
3Should I use the same Redis instance for sessions, cache, and Full Page Cache?
Technically possible, but only with separate database indices. Without that separation, a FLUSHDB for the cache accidentally deletes active sessions too.
4What does compress_data do in the env.php Redis configuration?
Enables compression of cached values, usually with gzip. Cuts memory usage by 50 to 70 percent depending on catalog size.
5When should I use AOF instead of RDB?
AOF everysec for the session store, because data loss there costs real customer sessions. RDB or no persistence for pure cache roles.
6What is the difference between allkeys-lru and volatile-lru?
allkeys-lru removes the least recently used keys regardless of TTL, volatile-lru only keys with a TTL set. Caches use allkeys-lru, sessions use volatile-lru.
7What happens when maxmemory-policy is set to noeviction and Redis runs full?
Redis rejects new write commands once maxmemory is reached. For Magento that means exceptions on page loads instead of gracefully evicting old entries.
8How do I calculate the Redis hit rate and what counts as a good value?
hits / (hits + misses) from INFO stats. Well above 90 percent for the object cache during normal operation; short dips after deployments are normal.
9Is Redis as a Full Page Cache backend a real alternative to Varnish?
Yes, for small and mid-sized stores. Under high traffic, Varnish wins, since cache hits are served without a PHP bootstrap.
10How do I prevent cache collisions between multiple Magento instances on the same Redis?
Unique persistent identifier or key prefix per instance, combined with separate database indices between staging and production.