CachingSeptember 2, 202612 min read

OOM command not allowed: Redis maxmemory and Eviction Policies

Redis is refusing writes and still serving reads. The error is not a crash, it is a policy doing exactly what you configured, and two defaults nobody chose put you there.

RThe Runsite Team

The application is up. Redis is up. Reads are fine. Every write returns the same line, and it has been returning it since about the time traffic picked up.

The first organic result for this error in Germany, as of writing, is a personal blog post from May 2016. Below it is a Stack Overflow question that is thirteen years old, then a YouTube video, then a GitHub issue from 2018 and a thread on the redis-db mailing list. It is a well-documented error with a decade of answers behind it, and most of those answers stop at the same two suggestions: give it more memory, or turn on eviction.

Both can be right. Which one is right depends on a question none of those pages ask first, and getting it backwards either loses data or papers over a leak. This walks the error apart, explains the two defaults that produce it, and gets to the question that actually decides the fix.

OOM command not allowed when used memory > 'maxmemory'.

That is the whole string, single quote and full stop included. Four facts are packed into it and three of them are commonly misread.

  • It is not a crash. Redis is running and answering. GET, EXISTS, TTL and DEL all still work. What has stopped is the class of commands that would make the dataset bigger, which is why the symptom reaches you as writes failing rather than as an outage.
  • It is not the Linux OOM killer. The names collide and the situations do not. The kernel's out-of-memory killer terminates a process and leaves you with a dead server and a line in dmesg. This is Redis declining a command on purpose, and the process is fine.
  • maxmemory is a limit you set, not a property of the machine. The error is Redis reporting that it reached a ceiling someone configured, which is either you, your configuration management, or the defaults of whatever image you deployed.
  • The quotes are part of the message. 'maxmemory' appears in single quotes in the error text itself, which matters only when you are grepping logs for it and wondering why an exact-match search returns nothing.

So the server is behaving correctly. It hit a limit and applied a policy. The interesting part is that in a default installation, nobody picked either of them deliberately.

The two defaults that produce this error

Redis ships with two settings whose defaults are individually reasonable and jointly surprising. As of writing, on a 64-bit build (Memcached reaches the same ceiling differently, with a limit that is mandatory and a policy you do not get to pick):

ini
# redis.conf — the two lines this error is about

# No limit. Redis will grow until something else stops it.
maxmemory 0

# When a limit exists, refuse writes rather than delete anything.
maxmemory-policy noeviction

Read them together and the sequence is clear. Out of the box there is no ceiling, so this error cannot happen; instead Redis grows until the kernel kills it, which is the failure people meet first. Then someone sets maxmemory to stop that, which is correct. The ceiling now exists, noeviction is still in place because nobody touched it, and the failure mode changes from a dead process to refused writes.

Neither default is wrong. noeviction is the safe choice for a Redis that holds something you cannot lose, and defaulting to silent data deletion would be worse. The problem is that the second setting is usually inherited rather than chosen, and it is the one that decides what your outage looks like.

Why maxmemory 0 is not the same as "no limit"

There is always a limit. Setting maxmemory 0 moves it from Redis to the operating system, where it is enforced by the kernel with no notice, no policy and no clean handling. A container with a memory limit will be killed at that limit; a virtual machine will start swapping first, which for an in-memory store is worse than being killed.

The Redis FAQ suggests a conservative rule for where to put the ceiling: "keep 20% of free memory available in the system beyond the configured maxmemory". The headroom is not padding. Replication buffers, the client output buffers and the copy-on-write pages produced during a background save all live outside the accounting that maxmemory governs, and they arrive exactly when the instance is busiest.

Eight policies, two axes

The documentation lists eight values for maxmemory-policy and most articles reproduce the list. It is easier to hold as a grid: first decide which keys are eligible for eviction, then decide how Redis picks among them.

Eligible keysLeast recently usedLeast frequently usedRandomShortest TTL
Every keyallkeys-lruallkeys-lfuallkeys-random
Only keys with a TTL setvolatile-lruvolatile-lfuvolatile-randomvolatile-ttl
None — refuse the write insteadnoevictionnoevictionnoevictionnoeviction
The eight values as a grid rather than a list. LFU variants were added in Redis 4.0; check the policies your version actually accepts before putting one in a config file.

Two details about the selection half are worth knowing before you rely on it. The LRU is approximate: rather than maintain a true ordering, Redis samples a handful of keys and evicts the best candidate from the sample, with the sample size controlled by maxmemory-samples. It is close enough for a cache and it is not a guarantee about any individual key. LFU counts accesses rather than recency, which is the better choice when a small set of keys is hot and a long tail is touched once and never again. Which of the two fits depends on how your code fills the cache, and the write pattern you chose largely decides that.

Redis maxmemory-policy noeviction but keys disappear without error

That is a Stack Overflow title, quoted as written, and it names the trap in the volatile-* half of the grid from the other direction.

A volatile-* policy can only evict keys that have a TTL. Plenty of cache code sets keys without any expiry, because the cache is expected to be replaced rather than to expire, and in that case the eligible set is empty. Redis has a policy, the policy has nothing to work with, and the behaviour collapses back to refusing writes. You get the noeviction outcome while the configuration says something else, which is the version of this problem that takes longest to diagnose.

One of the threads Google surfaces for this error is a user on the redis-db list reporting exactly the opposite confusion: "maxmemory is set to 500M with maxmemory-policy "volatile-lru", I'm setting TTL for each key sent". Both halves of the mistake are common. The rule to remember is that volatile-* is a promise about a subset, and you have to be the one who guarantees the subset is not empty.

The question that decides everything: cache or store?

Here is the fork that the top results skip. Before choosing a policy, answer one thing: if Redis silently dropped a random key right now, would anything be lost that does not exist somewhere else?

If the answer is no, Redis is a cache. Every key can be recomputed from the database or the origin, eviction is a cache miss and a cache miss is a slow request rather than an incident. allkeys-lru or allkeys-lfu is almost certainly what you want, and leaving noeviction in place on a pure cache means you have configured an outage to protect data that was never at risk.

If the answer is yes, Redis is a store, and this changes the whole shape of the fix. Sessions that exist nowhere else, a job queue, a rate-limiter's counters, a lock, the only copy of a computed result: evicting any of those is data loss that arrives without an error, because eviction is not an error. On a store, noeviction is the correct setting and the error you are reading is the system working. The fix is more memory, less data, or moving some of it out. Never a policy change.

The mixed instance is the real problem

Most instances that hit this are both at once: a cache and a queue and a set of sessions in one keyspace, because they started as a cache and grew. Rate-limit counters are the classic stowaway, and an evicted counter means the limit silently stops applying. There is no policy that is right for a mixed instance, which is the actual finding. Separate the durable data onto its own instance or database and the policy question answers itself on each side. What belongs in a cache in the first place is the version of this decision made early, when it is cheap.

Diagnosing it before it happens

Two INFO sections tell you everything relevant, and the second one is the one worth alerting on.

bash
redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy|mem_fragmentation_ratio'

# used_memory_human:3.71G
# maxmemory_human:4.00G
# maxmemory_policy:noeviction
# mem_fragmentation_ratio:1.19

redis-cli INFO stats | grep -E 'evicted_keys|keyspace_misses'

# evicted_keys:0
# keyspace_misses:184122

evicted_keys is the number that separates the two situations above without needing an opinion. On a cache with an allkeys-* policy it should be non-zero and rising steadily, which means the policy is doing its job; a sudden climb means your working set outgrew the instance. On an instance where it sits at zero while memory approaches the ceiling, you are on noeviction and the write refusals are queued up behind that number.

The useful alert is on the ratio of used_memory to maxmemory rather than on the error itself, because by the time the error appears users have already seen it. mem_fragmentation_ratio is worth a glance in the same breath: a value well above 1 means the allocator is holding memory the dataset is not using, and the ceiling arrives sooner than the key count suggests.

Eviction is not deletion, and your DPO will ask

Caches hold personal data. Sessions hold user identifiers, cached API responses hold whatever the API returned, and a rate limiter is a record of who did what and when. That makes eviction policy a question with a second audience.

An eviction policy is not a retention policy and it is not a mechanism for erasure. volatile-ttl looks like retention because it involves expiry times, but it removes whatever is closest to expiring when memory runs short, so the order is driven by pressure rather than by obligation. If a user asks for their data to be erased, an eviction policy has no opinion on the matter and may hold their session for another week while dropping someone else's.

Two practical consequences. Whatever you would say to an auditor about how long cached personal data lives has to come from explicit TTLs and explicit deletion on the erasure path, not from the eviction policy. And the instance holding that data is in a jurisdiction: where the data physically rests and who is a processor for it is a separate question from how it is evicted, and the answer to the first one is not improved by the second.

Not this error: "oom prevention" in other tools

If you arrived here from a video editor

One of the searches Google associates with this error is "Oom command not allowed under oom prevention opus clip", which is a different product's message entirely — Opus Clip is a video tool with its own out-of-memory guard. Nothing on this page applies to it. The Redis error always contains the words when used memory > 'maxmemory'; if yours does not, you are looking at a different system that borrowed the same three letters.

How Runsite handles it

On managed Redis hosted in the EU, the memory ceiling comes from the plan rather than from a config file nobody edited, so the maxmemory 0 half of this problem does not arise: there is a defined limit, it is visible, and the instance is monitored against it rather than against the machine. What the ceiling does not decide for you is the policy, because that depends on what you put in there, and the cache-or-store question above is one only your application can answer.

Servers are in Germany as of writing, and a signed GDPR data processing agreement covers every plan including the free one, which matters here specifically because sessions and cached responses are the personal data the previous section is about. If the honest answer to the cache-or-store question turns out to be "store", managed PostgreSQL in the EU is the cheaper place to keep the part that must survive, and Redis goes back to being a cache.

Being fair about it: nothing here is unique to a managed service. A maxmemory value, a policy chosen on purpose, an alert on the memory ratio and a look at evicted_keys get you the same result on your own server. The difference is whose calendar the first three land on. Setup details are in the Runsite docs.

The short version

  • OOM command not allowed when used memory > 'maxmemory'. is not a crash and not the Linux OOM killer. Redis is running, reads are being served, and commands that would grow the dataset are being declined on purpose.
  • Two defaults produce it. maxmemory 0 means no Redis-level limit, so the kernel enforces one instead; once you set a real ceiling, the untouched maxmemory-policy noeviction turns the failure from a dead process into refused writes.
  • There is always a limit. maxmemory 0 only decides whether it is enforced by Redis with a policy or by the kernel without one.
  • Leave headroom. The Redis FAQ suggests keeping 20% of system memory free beyond the configured maxmemory, because replication buffers, client output buffers and background-save copy-on-write pages sit outside that accounting.
  • The eight policies are a grid, not a list: which keys are eligible (allkeys-*, volatile-*, or none) crossed with how one is picked (lru, lfu, random, ttl).
  • A volatile-* policy with no TTLs anywhere behaves exactly like noeviction, and that is the version of this that takes longest to find.
  • The LRU is approximate. Redis samples candidates rather than maintaining a true ordering, which is right for a cache and is not a promise about any one key.
  • Answer cache-or-store before touching the policy. On a pure cache, noeviction is an outage protecting data that was never at risk. On a store, noeviction is correct and the fix is memory, not a policy change.
  • A mixed instance has no correct policy. Split the durable data out and each side answers for itself.
  • Alert on the ratio of used_memory to maxmemory, and watch evicted_keys: zero while memory climbs means you are on noeviction and the refusals are coming.
  • Eviction is not erasure. It removes what is convenient, not what you are obliged to delete, so retention has to come from explicit TTLs and an explicit deletion path.
FAQ

Frequently Asked Questions

Common questions about this service.

In Redis, start by deciding what the instance is for, because the two fixes are opposites. If everything in it can be recomputed from another source, it is a cache: set `maxmemory-policy` to `allkeys-lru` or `allkeys-lfu` and Redis will make room by evicting the least useful keys, turning the error into cache misses. If any of it exists nowhere else — sessions, a job queue, rate-limiter counters, a lock — then evicting is data loss, `noeviction` is the correct setting, and the fix is to raise `maxmemory`, reduce what you store, or move the durable part elsewhere. Two things to check before either: whether `maxmemory` is set at all, since a default of 0 means the kernel enforces the limit instead, and whether a `volatile-*` policy is configured against keys that have no TTL, which makes it behave like `noeviction` while appearing to be something else.

Measure before changing anything. `INFO memory` gives you `used_memory_human` against `maxmemory_human` and the policy in force; `INFO stats` gives you `evicted_keys`, which tells you whether eviction is happening at all. `mem_fragmentation_ratio` well above 1 means the allocator is holding memory the dataset is not using, so the real dataset may be smaller than the number suggests. From there the usual causes are keys written without a TTL that were expected to be temporary, values that grew larger than intended, and a working set that has genuinely outgrown the instance. Setting an explicit `maxmemory` with an eviction policy that matches what the data is for converts an unbounded problem into a bounded one, which is the point rather than a workaround. Leave headroom beyond the ceiling: the Redis FAQ suggests keeping about 20% of system memory free, because replication and client output buffers are not counted inside it.

`maxmemory` is a Redis configuration directive that sets the maximum amount of memory the dataset may occupy, in bytes or with a suffix such as `2gb`. Its default on 64-bit builds is 0, which means no Redis-level limit at all. When the limit is reached, Redis consults `maxmemory-policy` to decide what to do: evict keys to make room, or refuse commands that would increase memory use and return the OOM error. The accounting covers the dataset rather than the whole process, so replication buffers, client output buffers and the copy-on-write pages created during a background save can push actual usage above the configured figure. That is why the recommended practice is to set the ceiling below the memory available to the machine or container rather than equal to it.

There is no fixed ceiling in the software; on a 64-bit build the practical limit is whatever memory the machine or container makes available, and what you configure with `maxmemory` is a policy decision rather than a hardware one. The useful maximum is lower than the total for two reasons. Memory outside the dataset accounting needs room: replication buffers, client output buffers, and pages copied during a background save all sit there, which is where the Redis FAQ's suggestion of keeping 20% of system memory free comes from. And an in-memory store that starts swapping performs far worse than one that evicts, so the ceiling should be reached before the operating system runs short rather than after. If a single instance genuinely needs more than a large machine can hold, the answer is usually to split the keyspace across instances by purpose, which also resolves the mixed cache-and-store problem that produces most of these errors.

Your app deserves to be online

€5 of credit on signup. Deploy in under a minute. No credit card needed.