Databases

Redis maxmemory and Eviction on a Small Server

Set a Redis memory limit, choose an eviction policy that matches what the keys are for, and use INFO to tell whether Redis is evicting keys or refusing writes.

5 min readUpdated Tuesday, Oct 13, 2026
rediscacheconfigurationperformance

A misty autumn forest of birch and beech trees, with a leaf-covered path disappearing into pale light

On a 64-bit server, a fresh Redis has no memory limit, and its policy for hitting a limit is to evict nothing. That combination is perfectly calm right up until the VPS runs out of memory and the kernel picks something to kill.

The fix is two settings and one honest answer about what your keys are for. Here’s how to pick them, apply them so they survive a restart, and read what Redis reports afterwards.

What you have now

redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
1) "maxmemory"
2) "0"
1) "maxmemory-policy"
2) "noeviction"

0 means unlimited. noeviction means that once there is a limit and Redis reaches it, writes start failing while reads keep working. Debian 13’s packaged /etc/redis/redis.conf leaves both lines commented out, so these defaults are what you get there too.

A limit, with room to spare

maxmemory caps the memory Redis uses for data. It doesn’t cap everything the process uses:

  • Buffers for replicas and the AOF aren’t counted, so evicting keys doesn’t immediately fill them back up. INFO memory reports them as mem_not_counted_for_evict.
  • A background save forks the process. Pages that change during the save get copied, so a busy Redis can briefly need noticeably more than its dataset.
  • The limit is checked against Redis’s own allocation count (used_memory), not the RSS that top shows. Fragmentation lives in the gap between the two.

So the rule of thumb is to pick a number that leaves real headroom. On a 4 GB VPS that also runs an app and a database, a cache capped at 512 MB to 1 GB is a reasonable start. Check what Redis is using today:

redis-cli INFO memory | grep -E '^(used_memory_human|used_memory_peak_human|maxmemory_human|maxmemory_policy):'
used_memory_human:8.00M
used_memory_peak_human:8.02M
maxmemory_human:8.00M
maxmemory_policy:allkeys-lru

This small instance is sitting right at its limit, so every new write now costs an eviction.

The policy answers “who leaves?”

When Redis is at its limit and a write arrives, the policy decides which key gets thrown out to make room. Ask one question about your data: if this key vanished right now, would the app just rebuild it?

Every key is rebuildable (a pure cache). Use an allkeys-* policy:

  • allkeys-lru evicts the key that has gone longest without being read or written. The Redis docs call it a good default if you have no reason to pick something else.
  • allkeys-lfu evicts the key used least often. It holds on to steadily popular keys better. Picture a crawler touching every page once: under LRU, that burst of one-off keys can push the regulars out.

Some keys must survive (sessions or a job queue next to cache entries). Put a TTL on the rebuildable keys and use volatile-lru or volatile-lfu, which only evict keys that have an expiry set. The catch is that if no key has a TTL, there’s nothing to evict, and Redis behaves exactly like noeviction. The Redis docs also suggest a separate instance for this case, which is usually the calmer option.

No key is rebuildable. Keep noeviction. A failed write is loud, and data quietly disappearing from a queue is not.

The rest are more specialized. allkeys-random and volatile-random suit keys that are all accessed about equally. volatile-ttl evicts whatever expires soonest. Redis 8.6 added allkeys-lrm and volatile-lrm, which only count writes as “use”, so a key that’s read constantly but never updated still counts as stale. Older servers reject those two names, so check redis-server --version first. Debian 13, for example, ships 8.0.

LRU and LFU are both approximations: Redis samples a few keys (maxmemory-samples, default 5) and evicts the best candidate among them. That’s close enough for a cache, and it uses less memory than tracking the exact order would.

Apply it, then make it survive a restart

CONFIG SET takes effect on the next command:

redis-cli CONFIG SET maxmemory 512mb
redis-cli CONFIG SET maxmemory-policy allkeys-lru

If current usage is already above the new limit, the policy applies to the very next write. With allkeys-lru that means a wave of evictions. With noeviction, writes start failing. Lower the limit gently on a busy server.

CONFIG SET lives only in memory, so a restart puts the old values back. Either add the same two lines to redis.conf yourself:

maxmemory 512mb
maxmemory-policy allkeys-lru

or have Redis write its current settings into the file:

redis-cli CONFIG REWRITE

If you see ERR The server is running without a config file, there’s no file to rewrite. That’s normal for the official Docker image, which starts redis-server with no config file. Pass the settings as arguments instead, for example redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru as the container command.

Reading what happens next

redis-cli INFO stats | grep -E '^(expired_keys|evicted_keys|keyspace_hits|keyspace_misses):'
expired_keys:0
evicted_keys:26942
keyspace_hits:6358
keyspace_misses:6643

The hit rate is keyspace_hits / (keyspace_hits + keyspace_misses). Here that works out to about 49%, so half the reads miss. Put that next to almost 27,000 evictions and the picture is clear: this cache is too small for the keys the app keeps asking for. expired_keys counts keys that reached their TTL, which is a separate story. A high number there with few evictions points at TTLs that are too short.

The two failure modes look different:

  • OOM command not allowed when used memory > 'maxmemory'. Redis is at its limit and has nothing it’s allowed to evict. Either the policy is noeviction, or it’s volatile-* with no TTL keys left. INFO errorstats shows an errorstat_OOM count, and INFO commandstats shows rejected_calls for each command.
  • evicted_keys climbing while the hit rate falls. Redis is doing what you asked, just too often. Give it more memory, shorten what you cache, or try allkeys-lfu if a small set of keys gets most of the reads.

If evicted_keys stays at 0 while used_memory sits right at maxmemory, you’re on noeviction and one write away from the first bullet.

These counters run from startup or from the last CONFIG RESETSTAT. Compare them at two points in time rather than reading the totals as a rate.

If Redis also writes to disk, the RDB and AOF note covers what a fork costs and how to check that saves are working.

References

Cover photo: Unsplash.