# Redis OOM command not allowed: Set maxmemory-policy

> Source: <https://dev.to/acemeridian/redis-oom-command-not-allowed-set-maxmemory-policy-2nhn>
> Published: 2026-11-12 16:16:06+00:00

If Redis is only a cache, run `redis-cli CONFIG SET maxmemory-policy allkeys-lru` and put the same setting in redis.conf (or your managed service's parameter group): writes go through again, because Redis now evicts least recently used keys instead of refusing new ones. If the instance also holds queues, sessions or locks, keep `volatile-lru` and write every cache key with a TTL (`SET key value EX 3600`), because the volatile policies can only evict keys that have one.

This story was written with the assistance of an AI writing program. I reproduced the error on Redis 8.10.2 with redis-py 8.1.0 and Python 3.14.4, with `maxmemory` at 20 MB and 1 KiB values so the instance fills in seconds. Every number below comes from those runs.

Under the default policy, `noeviction`, the 15,201st `SET` failed. redis-py raised `redis.exceptions.OutOfMemoryError: command not allowed when used memory > 'maxmemory'.`; redis-cli prints the same text after `OOM`. `evicted_keys` was 0.

Not every command failed. `GET`, `EXISTS`, `EXPIRE` and `DEL` still worked; `SET`, `INCR` and `LPUSH` were refused. Redis rejects commands that can grow memory and lets reads and deletes through, so the outage looks partial: cache reads keep hitting while writes, counters and job enqueues fail. Deleting 1,000 keys let the next `SET` in.

Batches split too. A `MULTI`/` EXEC` with `EXPIRE`, `SET` and `INCR` was rejected whole, `EXPIRE` included. The same three commands in a plain pipeline applied the `EXPIRE` and failed the other two.

Self-hosted Redis defaults to `noeviction`; managed services often default to `volatile-lru`. I switched to `volatile-lru` and refilled with keys that had no TTL. The 15,200th `SET` failed with the same error and nothing was evicted: the volatile policies only choose among keys with an expiry, and there were none. Four commands show it:

``` bash
$ redis-cli CONFIG GET maxmemory-policy
maxmemory-policy
volatile-lru
$ redis-cli INFO keyspace
# Keyspace
db0:keys=15199,expires=0,avg_ttl=0,subexpiry=0
$ redis-cli INFO stats | grep evicted_keys
evicted_keys:0
$ redis-cli SET probe 1
OOM command not allowed when used memory > 'maxmemory'.
```

`expires=0` next to thousands of keys means the policy has nothing to evict. With `ex=3600` on every write, the same instance accepted 60,000 of 60,000 writes, evicted 45,155 keys and kept 14,845.

Under `allkeys-lru` with no TTLs, all 60,000 writes succeeded and 44,863 keys were evicted. None of the first 1,000 keys written were left; 982 of the last 1,000 were. For a pure cache, that is the behavior you want.

Then I put a 3,000-item list named `jobs` on the instance before the cache writes, the way small apps share one Redis between a task queue and a cache. Under `allkeys-lru` every cache write succeeded and the list was gone: `EXISTS jobs` returned 0, and nothing raised an error. Under `volatile-lru` with a TTL on each cache key, all 60,000 cache writes succeeded and the list kept its 3,000 items. Under `noeviction` the list survived, but cache writes failed after 12,489.

`allkeys-lru` belongs on an instance that holds nothing you cannot recompute. Anything else gets `volatile-lru` plus TTLs, or better, its own instance.

This is the write path I tested:

``` python
import redis
r = redis.Redis(host="localhost", port=6379)
def cache_set(key, value, ttl=3600):
    try:
        r.set(key, value, ex=ttl)  # a TTL makes the key evictable under volatile-lru
    except redis.exceptions.OutOfMemoryError:
        return False  # full and nothing evictable: skip the cache, keep serving
    return True
```

I ran it unchanged against a local Redis on port 6379. On a fresh `volatile-lru` instance it returned `True` 60,000 times out of 60,000 while Redis evicted 45,135 keys. On a full instance whose 15,199 existing keys had no TTL, it returned `False` 2,000 times out of 2,000: requests were still served, but nothing new was cached. The `except` keeps the app up, so count those `False` returns and alert on them.

On a full `noeviction` instance, `CONFIG SET maxmemory-policy allkeys-lru` took effect at once: the next 2,000 cache writes succeeded, with 2,051 evictions. A `CONFIG SET` is lost on restart, so persist the change in the config too.

If the instance has to stay on `volatile-lru`, you can add TTLs while it is full, because `EXPIRE` is not refused. I set a 600-second TTL on 3,000 keys that had none; all 3,000 calls succeeded, and the next 2,000 writes went through with 2,092 evictions. In production that is a `SCAN` over the cache key pattern with an `EXPIRE` per match.

With 12,000 keys, `used_memory` was 16,744,312 bytes, about 16 of the 20 MB. One 8 MB `SET` was still refused under `noeviction`, and `used_memory` was below the limit before and after. My reading is that the 8 MB argument is already in Redis's memory when it checks the limit, so the request counts against itself. Under `allkeys-lru` the same write succeeded after 4,794 evictions, and `used_memory` read 22,416,912 bytes right after, above the 20,971,520 limit. `maxmemory` is a target Redis evicts toward, not a hard cap, so leave headroom below the container or VM limit.

`CONFIG GET maxmemory-policy` and `INFO keyspace` first. `noeviction`, or a `volatile-*` policy with `expires=0`, means Redis has nothing it may evict.` allkeys-lru`, persisted in config, not only set at runtime.` volatile-lru` and a TTL on every cache write, or separate instances.`maxmemory` or less data, not a policy.`OutOfMemoryError` on cache writes and alert on it; reads keep working, so the outage looks partial.`MULTI` when writes must land together; a plain pipeline can apply half of them.
