A Redis instance with maxmemory set to 0 has no memory limit. On 64-bit builds, this is the default. Redis will keep allocating until the OS runs out of physical RAM, at which point the Linux OOM killer terminates the process. No warning, no graceful degradation, no eviction. The process simply vanishes.

What follows is a predictable restart cycle. Redis starts up, loads the last RDB snapshot (which may be stale or large enough to take minutes), and rejects all data commands during the loading phase. Applications see a cold cache with a 100% miss rate. They hammer the backing database to repopulate Redis. Clients reconnect in a thundering herd. If the dataset that caused the OOM is still being written, the instance hits the wall again and the cycle repeats.

This is the single most common production misconfiguration identified in the Redis operational playbook. Every production Redis instance must have maxmemory set, paired with the correct eviction policy, and sized to account for copy-on-write overhead during persistence.

What this means

maxmemory = 0 means Redis imposes no upper bound on its own memory use. The jemalloc allocator keeps requesting pages from the kernel until the system is exhausted. The kernel responds with the OOM killer, which scores processes by their memory footprint and terminates the highest-scoring one. Redis, typically the largest memory consumer, is the target.

The critical distinction operators miss: used_memory (what Redis tracks internally) and used_memory_rss (what the OS reports as resident set size) are not the same number. The OOM killer uses RSS, not used_memory. Fragmentation, jemalloc arena overhead, and copy-on-write (COW) during persistence forks all inflate RSS beyond what used_memory reports. An instance can have used_memory well under any limit you might imagine and still get OOM-killed because RSS exceeded available RAM.

During RDB snapshots and AOF rewrites, Redis forks a child process. With heavy write traffic, COW page duplication can push RSS to roughly 2x used_memory. This COW spike is not reflected in used_memory and is not counted against maxmemory. It is the most common cause of OOM kills in containerized Redis deployments.

flowchart TD
    A["maxmemory = 0"] --> B[Redis grows unbounded]
    B --> C{Persistence fork?}
    C -->|Yes| D[COW doubles RSS]
    C -->|No| E[Steady RSS growth]
    D --> F[OOM killer targets Redis]
    E --> F
    F --> G[Process killed]
    G --> H[Restart: load last RDB]
    H --> I[Cold cache + reconnect storm]
    I --> B

Common causes

CauseWhat it looks likeFirst thing to check
Default config untouchedCONFIG GET maxmemory returns 0; no memory limit ever enforcedredis-cli CONFIG GET maxmemory
maxmemory set equal to container limitPod OOM-killed before Redis reaches eviction; the limit matches the container memory cap exactlyCompare CONFIG GET maxmemory output to the container resources.limits.memory
CONFIG SET without CONFIG REWRITEChange was live but lost on restart; CONFIG GET maxmemory shows 0 after restartCheck the config file (often /etc/redis/redis.conf) for the maxmemory directive versus the running config
maxmemory-policy left at noevictionWrites fail with OOM command not allowed when used memory > 'maxmemory' instead of silent evictionCONFIG GET maxmemory-policy
Ignoring COW headroomInstance OOM-killed during BGSAVE or AOF rewrite despite used_memory being below maxmemoryCheck rdb_last_cow_size or aof_last_cow_size in INFO persistence

Quick checks

All commands below are read-only and safe to run on production instances:

# Check if maxmemory is set (0 means no limit)
redis-cli CONFIG GET maxmemory

# Check eviction policy
redis-cli CONFIG GET maxmemory-policy

# Current memory usage and fragmentation
redis-cli INFO memory | grep -E "used_memory:|used_memory_rss:|mem_fragmentation_ratio:"

# Check for OOM killer events in kernel log
dmesg -T | grep -i "out of memory\|oom-kill"

# Check if persistence is enabled (determines COW headroom needed)
redis-cli INFO persistence | grep -E "rdb_last_bgsave_status|aof_enabled"

# Check last COW size during fork operations
redis-cli INFO persistence | grep cow_size

# Check THP status (should be [never])
cat /sys/kernel/mm/transparent_hugepage/enabled

# Verify config file has the maxmemory directive
grep maxmemory /etc/redis/redis.conf

How to diagnose it

  1. Confirm maxmemory is set. Run CONFIG GET maxmemory. If the result is 0, the instance has no limit. This is the primary finding and the root cause of most unexplained Redis OOM kills.

  2. Check the eviction policy. Run CONFIG GET maxmemory-policy. The default is noeviction, which rejects writes with OOM errors instead of evicting keys. This is correct for data stores but wrong for caches.

  3. Compare maxmemory to system RAM. The recommended ceiling is roughly 75% of total system memory. For persistent instances (RDB or AOF enabled), the ceiling should be lower to accommodate COW. The playbook recommends keeping used_memory below 50% of physical RAM when persistence is enabled.

  4. Account for COW headroom. If persistence is enabled, the forked child process can temporarily double memory usage via copy-on-write. Check rdb_last_cow_size and aof_last_cow_size in INFO persistence. Values above 50% of used_memory indicate high OOM risk on the next fork.

  5. Check the container memory limit. In Kubernetes or Docker, the container limit must be at least 25% above maxmemory. If maxmemory is 4 GB, the container limit should be 5 GB or more. RSS can exceed used_memory due to fragmentation and COW, and the container runtime enforces the RSS limit, not Redis’s internal accounting.

  6. Look for OOM kills in kernel logs. Run dmesg -T | grep oom-kill. If redis-server appears in OOM kill records, the kernel terminated the process due to memory exhaustion.

  7. Verify THP is disabled. Transparent Huge Pages worsen COW behavior during fork. A single byte write to a 2 MB huge page copies the entire page. Check cat /sys/kernel/mm/transparent_hugepage/enabled and confirm it shows [never].

  8. Verify vm.overcommit_memory. Set to 1 for Redis to fork reliably. Without it, the kernel may refuse fork reservations, causing BGSAVE failures with fork: Cannot allocate memory. Check with sysctl vm.overcommit_memory.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
used_memory / maxmemoryProximity to the configured limitRatio above 80% warrants investigation; above 90% is imminent eviction or rejection
used_memory_rssWhat the OOM killer actually seesApproaching total system RAM or container memory limit
mem_fragmentation_ratioRSS vs used_memory efficiencyAbove 1.5 sustained indicates fragmentation waste; below 0.8 with used_memory above 100 MB indicates swap
evicted_keys rateKeys being removed due to maxmemorySustained non-zero rate for cache workloads; any eviction for data store workloads
rdb_last_cow_size / aof_last_cow_sizeMemory consumed by COW during forkAbove 50% of used_memory indicates high OOM risk on the next fork
total_error_replies / errorstat_OOMWrite rejections under noevictionAny non-zero rate of OOM errors means writes are silently failing
maxmemory value itselfWhether a limit exists at all0 in production is a critical misconfiguration

Fixes

Set maxmemory correctly

Set maxmemory in bytes. For a cache-only instance on a system with 16 GB of physical RAM, the ceiling is roughly 12 GB (75% of total). For persistent instances, lower this to 8 GB (50% of total) to leave room for COW during fork.

# Set maxmemory to 8 GB for a persistent instance on a 16 GB system
redis-cli CONFIG SET maxmemory 8589934592

# Persist the change to the config file
redis-cli CONFIG REWRITE

CONFIG REWRITE modifies the redis configuration file on disk. This is necessary so the change survives restarts. Without it, CONFIG SET changes are lost when the process restarts.

If your deployment method (Redis Operator, Helm chart, Docker Compose) injects maxmemory via environment variables or ConfigMap templates, verify the value actually reaches the running process after deployment.

Choose the right eviction policy

The eviction policy determines what happens when maxmemory is reached. The default noeviction rejects writes with OOM errors.

PolicyUse caseBehavior at maxmemory
noeviction (default)Data store, primary databaseRejects writes with OOM error; reads continue
allkeys-lruPure cacheEvicts least recently used keys
allkeys-lfu (Redis 4.0+)Cache with skewed access patternsEvicts least frequently used keys
volatile-lruMixed cache and persistent dataEvicts only keys with TTLs set
volatile-ttlCache with expiry-based priorityEvicts keys with shortest remaining TTL first
allkeys-lrm (Redis 8.6+)Read-heavy cache, stale data evictionEvicts least recently modified keys

For cache workloads, allkeys-lru or allkeys-lfu converts a hard write failure into silent eviction, which is the desired behavior. For data store workloads, keep noeviction but ensure capacity planning prevents the limit from being reached.

# Set eviction policy for a cache workload
redis-cli CONFIG SET maxmemory-policy allkeys-lru
redis-cli CONFIG REWRITE

Size for COW during persistence

If RDB or AOF persistence is enabled, the forked child process shares memory pages with the parent via copy-on-write. When the parent modifies a page, the kernel duplicates it for the child. Under heavy write load during fork, this can push RSS to approximately 2x used_memory.

The mem_not_counted_for_evict field in INFO memory shows the buffer memory excluded from eviction calculations. This prevents a feedback loop where evicting keys generates replication and AOF buffer writes that re-trigger eviction. However, these buffers still consume real RSS and count toward the OOM killer’s scoring.

Practical sizing rules from the playbook:

  • Cache-only instances (no persistence): maxmemory up to 75% of physical RAM. Leave 25% for bursts, fragmentation, and OS overhead.
  • Persistent instances (RDB or AOF enabled): used_memory should stay below 50% of physical RAM. This leaves headroom for COW during fork.
  • System RSS: used_memory_rss should stay below 75% of total physical RAM under all conditions.

Container and Kubernetes sizing

In containerized environments, the container memory limit must be higher than maxmemory. If maxmemory is 4 GB, set the container memory limit to 5 GB (25% above). This accounts for RSS overhead, fragmentation, and COW spikes during persistence.

The Kubernetes OOM killer acts on the pod’s cgroup memory limit, which includes RSS that Redis does not count toward maxmemory. Setting maxmemory equal to the container resource limit causes the pod to be OOM-killed before Redis itself triggers eviction. This is a well-documented issue with Redis Operators that inject maxmemory from ConfigMaps.

Kernel settings

Two kernel settings are essential for Redis with persistence:

  • vm.overcommit_memory = 1: Required for reliable fork() calls. Without it, the kernel may refuse fork reservations, causing BGSAVE to fail with fork: Cannot allocate memory. This setting is safe for Redis because COW means the child rarely needs the full reservation.

  • Disable Transparent Huge Pages: THP worsens COW behavior by 10-100x during fork. A single byte write to a 2 MB huge page copies the entire page. Disable it:

# Immediate effect (does not survive reboot)
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# Persistent across reboots
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local

Replica considerations

Replicas do not proactively evict keys based on maxmemory. Eviction and expiration events are replicated from the primary, but the replica itself does not initiate eviction when its own memory is under pressure. A replica with no limit will still grow unbounded and be OOM-killed. Set maxmemory on replicas for monitoring purposes, but rely on container limits and system-level controls for actual memory protection on replicas.

Prevention

  • Set maxmemory on every production instance. This is non-negotiable. Without it, there is no protection against unbounded growth.
  • Persist the config. Run CONFIG REWRITE after every CONFIG SET maxmemory. Without it, the change is lost on restart.
  • Pair maxmemory with the right eviction policy. noeviction causes write failures when memory is full. For caches, use allkeys-lru or allkeys-lfu.
  • Size for COW. If persistence is enabled, keep maxmemory at or below 50% of physical RAM. COW during fork can temporarily double RSS.
  • Set container limits above maxmemory. The pod memory limit should be at least 25% above maxmemory.
  • Disable THP and set vm.overcommit_memory=1. These prevent fork failures and COW amplification.
  • Monitor used_memory_rss alongside used_memory. The OOM killer uses RSS, not logical memory. A stable used_memory with growing RSS signals fragmentation that will eventually trigger an OOM kill.
  • Watch mem_fragmentation_ratio. Sustained values above 1.5 indicate fragmentation waste. Enable activedefrag yes (Redis 4.0+, requires jemalloc) to reduce fragmentation automatically.

How Netdata helps

  • Per-second used_memory and used_memory_rss collection reveals the exact moment RSS diverges from logical usage, catching fragmentation and COW spikes that precede OOM kills.
  • maxmemory-aware memory ratio charts surface proximity to the configured limit. When maxmemory is 0, the ratio is undefined, which immediately flags the misconfiguration.
  • COW size tracking via rdb_last_cow_size and aof_last_cow_size correlates with RSS spikes during persistence forks, helping you size headroom correctly before the next fork triggers an OOM.
  • Eviction rate and OOM error detection from evicted_keys and errorstat_OOM distinguishes between healthy cache turnover and a memory pressure spiral where eviction, cache misses, and re-population writes all climb simultaneously.
  • ML-based anomaly detection on mem_fragmentation_ratio flags fragmentation drift before RSS silently grows past the OOM threshold, especially on instances where used_memory appears stable.

Netdata’s Redis monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.