# Redis OOM command not allowed: What It Means

> Redis refuses writes but still serves reads. What maxmemory and noeviction do, which eviction policy you want, and when the fix is more memory.

1. [Home](/)
2. [Blog](/blog)
3. OOM command not allowed: Redis maxmemory and Eviction Policies

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.

## The short version

It is not a crash. It is an eviction policy doing what you configured, and two defaults nobody chose put you there. The fix depends on one question: if everything in the instance can be recomputed, it is a cache — set allkeys-lru or allkeys-lfu and the error becomes cache misses. If not, it needs more memory.

[Teo Marquardt](/about#author)

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](/blog/redis-vs-memcached), 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, and the container [exits with code 137](/blog/exit-code-137); 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 keys                   | Least recently used | Least frequently used | Random          | Shortest TTL |
| ------------------------------- | ------------------- | --------------------- | --------------- | ------------ |
| Every key                       | allkeys-lru         | allkeys-lfu           | allkeys-random  | —            |
| Only keys with a TTL set        | volatile-lru        | volatile-lfu          | volatile-random | volatile-ttl |
| None — refuse the write instead | noeviction          | noeviction            | noeviction      | noeviction   |

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](/blog/cache-aside-write-through-write-behind) 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. If sessions are the durable part, [where sessions should live in the first place](/blog/redis-vs-database-for-sessions) is worth deciding before memory runs out.

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](/blog/rate-limiting-429-retry-after). 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](/blog/when-to-use-redis-cache) 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](/blog/gdpr-data-residency) 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](/services/redis), 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 account, 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](/services/postgresql) 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](https://docs.runsite.app).

## 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.

Related service

## Managed Redis on Runsite

Deploy from a single git push and keep active apps warm — no cold starts, hosted entirely in the EU with a signed GDPR DPA on every plan.

[Explore Managed Redis](/services/redis)

[Back to all articles](/blog)

FAQ

## Frequently Asked Questions

Common questions about this service.

### How to fix OOM error?

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.

### How can I fix Redis using too much memory?

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.

### What is maxmemory?

\`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.

### What is the maximum memory a Redis instance can use?

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.

Keep reading

## Related articles

[Caching12 min readWhen to Use Redis as a Cache (and When It Makes Things Worse)A cache is a second copy of your data that is allowed to be wrong. This is the decision frame: what to cache, what never to, the four failures that only appear in production, and why a cache full of sessions is a compliance object.Aug 21, 2026Read](/blog/when-to-use-redis-cache)[Caching13 min readCache-Aside, Write-Through, Write-Behind: Choosing a Caching PatternSix names for what is really two decisions. The axes people conflate, the race that makes cache-aside delete instead of update, and why every canonical write-behind implementation is deprecated.Aug 24, 2026Read](/blog/cache-aside-write-through-write-behind)[Caching12 min readRedis vs Memcached: When the Simpler Cache Is Still the Right OneThe comparison everyone links to is a Stack Overflow question closed as opinion-based fourteen years ago. What has changed since then is not the feature list. It is the licence, the fork, and a demand curve falling 46% a year.Aug 27, 2026Read](/blog/redis-vs-memcached)

## Your app deserves to be online

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

[Start deploying](https://dashboard.runsite.app/login)[View documentation](https://docs.runsite.app)

---

Source: https://runsite.app/blog/redis-oom-command-not-allowed
