All posts
RedisAugust 4, 20268 min read

Redis Eviction Policy Guide: Choosing maxmemory-policy

Redis eviction policy explained: how maxmemory-policy works under sustained load, which of 8 options to pick, and how to avoid silent data loss in production.

F
Fepiq Team
Fepiq

The exact query engineers type into a search bar at 2 a.m. is usually some version of "redis eviction policy production" or "why is redis dropping my keys." The short answer: Redis only evicts keys because you told it to, either explicitly through maxmemory-policy or implicitly by leaving the default noeviction in place until writes start failing. Which of the eight policies is right for your service depends on whether Redis is a pure cache, a session or queue store, or a mix of both — and that answer changes again once traffic is sustained rather than bursty. This guide walks through the mechanics, the trade-offs, and a rollout checklist you can apply to a production cluster this week.

What actually happens when Redis hits maxmemory

Redis ships with maxmemory-policy set to noeviction. That is not a safe default for most SaaS workloads — it means once used_memory reaches maxmemory, Redis rejects every write command that would consume more memory (SET, LPUSH, HSET, SADD, and friends) with an OOM error, while reads keep working. If your application does not catch that error explicitly, a checkout flow, a session write, or a queue enqueue silently fails at exactly the moment traffic is highest, because that is when memory pressure peaks. Teams who never touch maxmemory-policy usually discover this the same way: a spike in 500s that correlates with a memory graph flatlining at the ceiling, not with CPU or network.

Eviction policies exist to make that failure mode a deliberate choice instead of an accident. Instead of rejecting the write, Redis frees space by deleting existing keys according to the policy you configure, then completes the write. The trade-off is explicit: you are choosing to lose some data to keep the service available, and the policy determines which data you lose first.

The eight maxmemory-policy options, compared

PolicyWhat it evictsBest for
noevictionNothing — rejects writes at the memory ceilingRedis as a system of record you cannot afford to lose (rare)
allkeys-lruLeast recently used key, any keyPure cache in front of a database, unsure traffic pattern
allkeys-lfuLeast frequently used key, any keyCache with a stable power-law access pattern (hot/cold split)
allkeys-randomRandom key, any keyUniform access patterns where recency/frequency carry no signal
volatile-lruLeast recently used key, only keys with a TTLMixed instance: some keys must survive, cache keys can go
volatile-lfuLeast frequently used key, only keys with a TTLMixed instance, frequency-based cache portion
volatile-randomRandom key, only keys with a TTLMixed instance, cheapest CPU cost, weak hit-rate signal
volatile-ttlKey closest to expiring, only keys with a TTLMixed instance where near-expiry keys are safest to drop early

In practice, production traffic settles on two policies almost every time. If the Redis instance is exclusively a cache — API responses, rendered fragments, computed aggregates — allkeys-lru is the pragmatic default: it needs no per-key TTL bookkeeping, it is memory-efficient because you do not pay the ~8 extra bytes per key for an expiry field on every entry, and it degrades gracefully under a typical Zipfian (power-law) access pattern where a small set of keys absorbs most of the traffic. If the same instance also holds session tokens, rate-limit counters, or queue metadata that must not be evicted, volatile-lru with TTLs set only on the cache keys keeps the durable keys off the eviction table entirely, because volatile- policies never touch keys without an expiry.

Match the policy to the workload, not the default

  • Cache-only (API/query cache, fragment cache): allkeys-lru, or allkeys-lfu if your dashboards show a stable long-tail of rarely-reused keys that LRU keeps evicting too slowly.
  • Session store: volatile-lru or volatile-ttl, with an explicit EXPIRE on every session key — never noeviction, since a full instance should degrade to shorter sessions, not failed logins.
  • Job queue (BullMQ, Laravel Horizon, Sidekiq-style lists/streams): noeviction on a dedicated instance separate from the cache. Losing a queued job silently is worse than a write erroring loudly, so isolate queue data from cache data at the instance or cluster level rather than trusting a shared policy to protect it.
  • Rate limiting / counters: volatile-lru with short TTLs; losing a counter early just resets a window slightly sooner, which is a safe failure mode.
  • Mixed instance you cannot split yet: volatile-lru as a bridge, with a migration plan to separate cache and durable data into different logical databases or clusters.

Sustained load changes the calculus

Eviction policy choices that look fine in staging often break down under weeks of sustained production traffic for three reasons. First, LRU in Redis is approximated, not exact — by default Redis samples 5 keys (maxmemory-samples) and evicts the oldest of that sample, which is cheap but means true "least recently used" accuracy degrades as your keyspace grows into the tens of millions. Raising maxmemory-samples to 10 improves accuracy at a small CPU cost and is worth it once a single node holds more than a few million keys under real eviction pressure. Second, memory fragmentation compounds over time: Redis's allocator (jemalloc by default) can leave used_memory reporting well below what the OS actually has resident, so mem_fragmentation_ratio creeping toward 1.5 or higher under sustained load means eviction is being triggered later than it should, or not accurately reflecting real headroom — that is a signal to schedule a rolling restart or upgrade Redis, not to loosen the eviction policy further. Third, sustained load exposes hot keys: a policy tuned for an even distribution will thrash if 1% of keys take 90% of traffic, because LRU eviction and the active-expire cycle both compete for CPU with the read/write load on those same hot keys, adding latency spikes exactly when you can least afford them.

The practical fix for hot-key thrash under an eviction policy is usually smaller and shorter-lived cache entries with jittered TTLs (so keys do not all expire in the same millisecond and stampede the origin), not a different eviction algorithm. Combine that with client-side caching (RESP3 tracking) for the hottest handful of keys so Redis is not even asked for them on every request.

Monitoring: catch evictions before they become an outage

Every eviction policy other than noeviction is, by definition, deleting data your application did not ask to delete. The only way to know whether that is happening at an acceptable rate is to watch it directly rather than infer it from error rates.

  1. Track evicted_keys from INFO memory (or Redis Exporter for Prometheus) as a rate, not a cumulative counter — a sudden slope change is the earliest signal of memory pressure, well before OOM errors appear.
  2. Alert on used_memory approaching maxmemory (e.g. 85%) so you have time to scale before eviction rate spikes, not after.
  3. Watch keyspace_misses alongside evicted_keys — rising misses with rising evictions means your working set no longer fits in memory and you need more capacity, not a smarter policy.
  4. Track mem_fragmentation_ratio; sustained values above 1.5 mean real usable memory is lower than the raw metric suggests.
  5. For volatile- policies, confirm with DBSIZE vs the count of keys with a TTL that your durable keys actually have no expiry set — a missing EXPIRE call on a key you meant to protect is the most common production incident tied to eviction policy.
Eviction policy is not a set-and-forget config line. It's a promise about which data your system is allowed to lose under pressure — and that promise should be tested the same way you test failover, not discovered during an incident.
Fepiq Engineering

Rollout checklist for changing maxmemory-policy in production

  1. Audit which keys currently have a TTL vs none, and confirm that split matches your intended cache vs durable-data boundary.
  2. Set maxmemory explicitly (never rely on the OS default) and leave 20-25% headroom below the container or instance memory limit for fragmentation and replication buffers.
  3. Change the policy on a replica first if using replication, verify behavior under a synthetic load test, then promote.
  4. Set maxmemory-samples to 10 if your keyspace exceeds a few million keys and CPU headroom allows it.
  5. Add evicted_keys, used_memory percentage, and mem_fragmentation_ratio to your existing dashboards before the change ships, not after.
  6. Re-test the change under your actual peak traffic pattern, not average load — eviction behavior is a sustained-load problem, and averages hide it.

None of this replaces the broader scaling work — sharding, replica topology, connection pooling — that keeps a Redis cluster healthy as traffic grows; eviction policy is one layer of that stack, and it's often the layer teams configure once during setup and never revisit as data volume and traffic pattern change underneath it.

Eviction policy is just one piece of running Redis reliably at scale. See how the rest of the stack — clustering, hot keys, and persistence trade-offs — fits together.

Read: Scaling Redis for High-Traffic SaaS

Frequently asked questions

What is the best Redis eviction policy for a cache?+

allkeys-lru is the safest default for a pure cache: it requires no per-key TTL management, is memory-efficient, and handles the power-law access pattern most caches see. Switch to allkeys-lfu only if your data shows a stable long-tail that LRU evicts too slowly, and verify the switch with a hit-rate comparison under real traffic.

Why does Redis reject writes instead of evicting keys automatically?+

Because the default maxmemory-policy is noeviction. Redis will not delete data on your behalf unless you explicitly opt into a different policy, since deleting data is a business decision, not a purely technical one. Most production caches should not run on noeviction.

Can I use different eviction policies for cache and session data on the same Redis instance?+

Not per-key, but volatile- policies only evict keys with a TTL set, so you can protect session or queue keys by never setting an expiry on them while letting cache keys carry a TTL and evict freely. For stricter isolation, separate cache and durable data into different Redis instances or logical databases.

How do I know if my eviction policy is causing problems in production?+

Watch the evicted_keys rate alongside keyspace_misses and used_memory as a percentage of maxmemory. A rising eviction rate combined with rising cache misses means your working set has outgrown available memory and needs more capacity, not just a policy change.

Does changing maxmemory-samples improve eviction accuracy?+

Yes. Redis approximates LRU/LFU by sampling a small number of keys (5 by default) and evicting the oldest of that sample. Raising maxmemory-samples to 10 gets closer to true LRU behavior at a modest CPU cost, which matters once a node holds several million keys under sustained eviction pressure.

Running Redis under production load and not sure your eviction and memory strategy will hold at 10x traffic? Fepiq designs and audits Redis architecture for growing SaaS teams.

Talk to Fepiq about your Redis architecture

Get new posts in your inbox

Occasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.

Keep reading

Related posts

All posts

Let's build something

Ready to ship your next product with Fepiq?

Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.