skip to content

A Redis instance reaches the `maxmemory` limit configured in its config file, and the deployment never changed `maxmemory-policy` from its default. What happens to the commands clients send after that point, and what error do they see?

level: middleimportance: must knowfreq 68%

answer

  1. Default policy = noeviction
  2. OOM command not allowed > maxmemory
  3. denyoom flag: writes fail, reads pass
  4. maxmemory 0 = unlimited on 64-bit
  5. evicted_keys stays 0 under noeviction

basics

~20 s

The default policy is noeviction: Redis deletes nothing. Reads still work, but any command that would use more memory is rejected with an error starting "OOM command not allowed when used memory > 'maxmemory'". Keys with TTLs still expire normally.

solid answer

~50 s

`maxmemory` caps the memory Redis reports as used for its dataset. When usage reaches the cap, Redis applies `maxmemory-policy`, whose default is **noeviction** — it evicts nothing. Under noeviction, commands flagged as memory-increasing (SET, LPUSH, HSET, INCR, SADD, and friends) fail with `-OOM command not allowed when used memory > 'maxmemory'`. Read commands (GET, LRANGE, EXISTS) and memory-freeing commands (DEL, UNLINK, EXPIRE, LPOP) still succeed, so the instance is not dead — it is read-only-ish. Keys with a TTL keep expiring, which may free enough room for writes to resume on their own, which is why the symptom often looks intermittent. The practical trap is that `maxmemory` defaults to 0 (unlimited) on 64-bit builds, so an unbounded instance never OOM-errors; it grows until the OS OOM-killer or swap ruins it. "Redis is a cache so it drops old data automatically" is false until you choose an eviction policy.

code

text · 9 lines
text
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> SET user:42 "payload"
(error) OOM command not allowed when used memory > 'maxmemory'.
127.0.0.1:6379> GET user:41
"still readable"
127.0.0.1:6379> DEL user:41
(integer) 1

go deeper

for a junior

Know that maxmemory sets a budget, that the default policy deletes nothing, and that writes then return an OOM error while reads keep working.

for a middle

Explain the denyoom distinction, that TTL'd keys still expire so errors can come and go, and that maxmemory 0 means unlimited on 64-bit builds.

for a senior

Triage from INFO memory/INFO stats, know RSS exceeds maxmemory because of fragmentation, buffers and fork copy-on-write, and pick the right remediation instead of blindly raising the limit.

for a principal

Frame the default as a deliberate safety choice for mixed cache/system-of-record use, and set instance-level policy from what the data actually is — including whether that data belongs in this instance at all.

## What `maxmemory` is `maxmemory` is a configuration directive giving Redis a byte budget for the data it holds, e.g. `maxmemory 4gb`. It is a soft accounting limit, not an OS-enforced one: Redis compares its own `used_memory` figure against the limit before executing commands that can allocate. The accounting deliberately subtracts some transient buffers (replica output buffers, the AOF buffer), so the process RSS you see in `top` is normally **larger** than `maxmemory` — that gap matters when you size a container. On 64-bit builds the default is `maxmemory 0`, meaning "no limit". An instance with no limit cannot OOM-error; it simply keeps allocating until the kernel's OOM killer terminates it or the box starts swapping, at which point latency collapses because Redis touches memory on every command. Setting a limit is therefore the first production step, not an optimization. ## What happens at the limit When used memory reaches `maxmemory`, Redis consults `maxmemory-policy`. The default value is **noeviction**, which means: delete nothing, refuse to grow. Concretely, every command is tagged in the command table with a `denyoom` (use-memory) flag. Before executing a `denyoom` command while over the limit, Redis returns the error reply: `(error) OOM command not allowed when used memory > 'maxmemory'.` Commands without that flag run normally. So: - **Still works:** GET, MGET, EXISTS, LRANGE, HGETALL, TTL, SCAN, INFO, and all read paths. - **Still works:** DEL, UNLINK, LPOP, SPOP, EXPIRE, LTRIM — anything that frees or cannot grow memory. - **Fails:** SET, SETEX, INCR, LPUSH, HSET, SADD, ZADD, XADD, RPOPLPUSH's push side, and any script or MULTI containing them. Because TTL'd keys keep expiring in the background, memory can drop back under the limit on its own; writes then start succeeding again. Clients experience this as a flapping stream of OOM errors rather than a hard outage, which is why the condition is often misdiagnosed as a network or client-library bug. ## Why noeviction is the default Redis is used both as a cache and as a system of record for things like sessions, queues and dedupe sets. Silently deleting a user's session or a queued job to make room for a cache entry would be a data-loss bug, so the shipped default fails loudly instead. Choosing an eviction policy is an explicit statement that *everything in this instance is disposable* (`allkeys-*`) or that *only the keys I gave a TTL are disposable* (`volatile-*`). ## Diagnosing it `INFO memory` gives `used_memory`, `used_memory_rss`, `maxmemory`, `maxmemory_policy` and `mem_fragmentation_ratio`. `INFO stats` gives `evicted_keys` — under noeviction it stays at 0 no matter how bad things get, so a zero counter is not evidence of health. Client-side, count OOM errors: a well-behaved application should surface them as a distinct metric rather than as generic "redis error", because the remediation is completely different from a timeout. ## Fixing it There are only four real moves, in rough order of preference: 1. **Give the data TTLs** and switch to a `volatile-*` policy so genuinely disposable data is reclaimed. 2. **Declare the instance a pure cache** and switch to an `allkeys-*` policy. 3. **Shrink the data** — bad key design (giant hashes, unbounded lists, oversized serialized blobs) is usually the actual cause. 4. **Raise `maxmemory` / add nodes**, keeping headroom below the container limit for fragmentation, client buffers and fork copy-on-write during RDB/AOF-rewrite. Note that changing policy is live: `CONFIG SET maxmemory-policy allkeys-lru` takes effect immediately and Redis starts evicting on the next command that needs room — a useful emergency lever, but only if you are certain nothing in the keyspace is irreplaceable. Persist the change to the config file (or `CONFIG REWRITE`) or it vanishes on restart.

  • Under noeviction and over the limit, which commands still succeed, and why?
    Every command carries a `denyoom` flag in Redis's command table marking it as potentially memory-increasing; only those are refused. Reads (GET, LRANGE, SCAN), introspection (INFO, TTL) and memory-freeing commands (DEL, UNLINK, LPOP, EXPIRE) lack the flag and run normally. That is why an over-limit instance behaves like a read-mostly store rather than being fully down, and why you can always DEL your way back under the limit.
  • If `maxmemory` is left at its default of 0, what happens instead?
    Nothing inside Redis stops it: it keeps allocating until the operating system intervenes. On a container you get an OOM-kill and a hard restart with loss of anything not persisted; on a VM with swap enabled you get thrashing, because Redis randomly touches pages and every swapped page becomes a disk read on the command path, so p99 latency explodes. Always set an explicit limit below the container/VM memory, leaving headroom for RSS overhead and fork copy-on-write.
  • Why does the process RSS exceed `maxmemory` even when Redis says it is under the limit?
    The maxmemory check uses Redis's own `used_memory` accounting, which excludes replica output buffers and the AOF buffer, and it cannot account for allocator fragmentation or the copy-on-write pages of a background save. So RSS is routinely 1.2–1.5x the configured limit, more during an RDB fork or a big rewrite. Size the container with that headroom, or the kernel will kill a process that believes it is within budget.

saying these in an interview costs you the question

  • Saying Redis evicts old keys automatically by default — the default is noeviction.
  • Claiming reads also fail with OOM; only memory-increasing commands are refused.
  • Assuming Redis spills to disk or swaps data out when full — RDB/AOF are durability mechanisms, not overflow storage.
  • Reading the OOM error as an operating-system OOM-kill; it is a Redis-generated error reply.
  • Trusting `evicted_keys = 0` as proof memory is healthy under noeviction.

context