cd /news/ai-tools/redis-oom-command-not-allowed-set-ma… · home › topics › ai-tools › article
[ARTICLE · art-168437] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Redis OOM command not allowed: Set maxmemory-policy

A developer reproduced Redis's "OOM command not allowed" error on Redis 8.10.2 with redis-py 8.1.0 and Python 3.14.4, finding that the default noeviction policy rejected the 15,201st SET while reads and deletes kept working. Testing showed allkeys-lru accepted all 60,000 writes but silently evicted a 3,000-item jobs list, while volatile-lru with a TTL on every cache key accepted all 60,000 writes and preserved the list. The developer recommends allkeys-lru only for instances holding nothing that cannot be recomputed, and volatile-lru plus TTLs otherwise.

by read4 min views2 publishedNov 12, 2026

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:

$ redis-cli CONFIG GET maxmemory-policy
maxmemory-policy
volatile-lru
$ redis-cli INFO 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:

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.

── more in #ai-tools 4 stories · sorted by recency
── more on @redis 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/redis-oom-command-no…] indexed:0 read:4min 2026-11-12 · —