Redis Eviction on a Small VPS: maxmemory-policy, Approximated LRU, and the Buffers That Steal Your Budget

Most Redis tutorials stop at maxmemory. The interesting part begins after it: which key gets evicted, why sample-based approximation sometimes deletes the object you just wrote, and how Redis 7’s maxmemory-clients and shared-connection buffers count against the same ceiling. On a 512 MB or 1 GB VPS, these details decide whether you see a 96% hit rate or a 60% one under identical load.

Establish the real eviction budget

Redis does not reserve memory for the dataset alone. Subtract the following from your maxmemory before sizing the working set:

  • Replication backlog: repl-backlog-size (default 1 MB; 10 MB per replica is common in production).
  • Client output buffers governed by client-output-buffer-limit.
  • The AOF buffer plus rewrite buffer during a BGREWRITEAOF.
  • Up to two copies of the dataset during a fork-based RDB save (copy-on-write, so proportional to write volume, not dataset size).

Measure what is actually resident with redis-cli info memory and compare used_memory_rss against used_memory. A persistent gap above 40% usually means heavily fragmented large values or an oversized client-output-buffer-limit. Set maxmemory to leave at least 20–25% headroom for these buffers and the allocator, or eviction fires while RSS still climbs.

Eviction is approximated, and that matters

Redis LRU and LFU are approximated, not exact. Each policy samples maxmemory-samples keys (default 5) and evicts the best candidate among them. With a small sample, a key you just wrote can be evicted immediately after a write if the sampled set happens to contain only older keys. Raising maxmemory-samples to 10 improves the approximation at a small CPU cost — a worthwhile trade on a small VPS where a 60% hit rate costs more than the extra sampling does. LFU (allkeys-lfu, volatile-lfu) additionally tracks access frequency with a logarithmic counter, which resists the scan pollution that defeats LRU when a client walks a large keyspace.

Which policy fits which workload

  • noeviction: writes fail with OOM once full. Correct only when Redis is a durable store, not a cache, and you would rather error than lose data.
  • allkeys-lru: default choice for a pure cache with uniform value per key.
  • volatile-lru: only keys with a TTL are candidates. Dangerous if most keys have no TTL — Redis will refuse writes with used_memory > maxmemory while barely evicting anything.
  • allkeys-lfu: best for skewed workloads where a small hot set must survive; the safe upgrade from allkeys-lru when scan traffic pollutes the cache.

Key design decisions that change eviction

How you structure keys changes what eviction can do. Packing a large logical object into one key means that object is evicted as a unit — you either keep the whole thing or lose it, and an oversized value can even be evicted immediately after a write because it exceeds what the sampled set deems cold. Splitting into many smaller keys gives the approximation finer granularity and more even eviction, at the cost of more keyspace metadata. TTLs are the other lever: a well-chosen TTL lets volatile-* policies reclaim deterministically instead of relying on sampling. If your data has natural expiry, prefer a volatile policy and set explicit TTLs on every key; if it does not, allkeys-lfu is the safer default. Never mix — a cache where half the keys have no TTL will confuse any volatile policy.

Active defragmentation and lazy free

Two features interact with eviction on small instances. activedefrag yes lets Redis reclaim fragmentation online, which reduces the RSS/used_memory gap that otherwise forces you to lower maxmemory unnecessarily — but it costs CPU, so enable it only when mem_fragmentation_ratio stays above 1.4 and you have spare cycles. lazyfree-lazy-eviction yes (Redis 4+) frees evicted keys on a background thread instead of blocking the main loop, which smooths latency spikes during eviction storms. On a busy 1 GB VPS these two settings frequently do more for tail latency than any change to the policy itself. Confirm the effect with redis-cli --latency-history before and after: the p99 latency during heavy eviction is the number to watch.

Verify the policy is doing what you think

redis-cli config get maxmemory-policy
redis-cli info stats | grep -E 'evicted_keys|keyspace_hits|keyspace_misses'
redis-cli info memory | grep -E 'used_memory_human|used_memory_rss_human|mem_fragmentation_ratio'

Watch the ratio of evicted_keys to keyspace_hits: a rising eviction rate alongside a falling hit rate means the working set no longer fits. That is a capacity decision, not a tuning one — no policy recovers memory you never had. Compute the hit rate as hits / (hits + misses) over a steady window; if it is below your target, resize the plan before chasing policy tweaks. Our performance VPS plans lists plans by RAM tier so you can match the working set to the instance. And always set maxmemory before any policy: with it unset, Redis grows until the host OOM killer takes the process. If the data tells you the working set genuinely needs more room, our VPS comparison page maps instance sizes to realistic cache capacities so you size once instead of evicting forever.

Leave a Reply