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 --> BCommon causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Default config untouched | CONFIG GET maxmemory returns 0; no memory limit ever enforced | redis-cli CONFIG GET maxmemory |
| maxmemory set equal to container limit | Pod OOM-killed before Redis reaches eviction; the limit matches the container memory cap exactly | Compare CONFIG GET maxmemory output to the container resources.limits.memory |
| CONFIG SET without CONFIG REWRITE | Change was live but lost on restart; CONFIG GET maxmemory shows 0 after restart | Check the config file (often /etc/redis/redis.conf) for the maxmemory directive versus the running config |
| maxmemory-policy left at noeviction | Writes fail with OOM command not allowed when used memory > 'maxmemory' instead of silent eviction | CONFIG GET maxmemory-policy |
| Ignoring COW headroom | Instance OOM-killed during BGSAVE or AOF rewrite despite used_memory being below maxmemory | Check 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
Confirm maxmemory is set. Run
CONFIG GET maxmemory. If the result is0, the instance has no limit. This is the primary finding and the root cause of most unexplained Redis OOM kills.Check the eviction policy. Run
CONFIG GET maxmemory-policy. The default isnoeviction, which rejects writes with OOM errors instead of evicting keys. This is correct for data stores but wrong for caches.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_memorybelow 50% of physical RAM when persistence is enabled.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_sizeandaof_last_cow_sizeinINFO persistence. Values above 50% ofused_memoryindicate high OOM risk on the next fork.Check the container memory limit. In Kubernetes or Docker, the container limit must be at least 25% above
maxmemory. Ifmaxmemoryis 4 GB, the container limit should be 5 GB or more. RSS can exceedused_memorydue to fragmentation and COW, and the container runtime enforces the RSS limit, not Redis’s internal accounting.Look for OOM kills in kernel logs. Run
dmesg -T | grep oom-kill. Ifredis-serverappears in OOM kill records, the kernel terminated the process due to memory exhaustion.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/enabledand confirm it shows[never].Verify vm.overcommit_memory. Set to
1for Redis to fork reliably. Without it, the kernel may refuse fork reservations, causing BGSAVE failures withfork: Cannot allocate memory. Check withsysctl vm.overcommit_memory.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
used_memory / maxmemory | Proximity to the configured limit | Ratio above 80% warrants investigation; above 90% is imminent eviction or rejection |
used_memory_rss | What the OOM killer actually sees | Approaching total system RAM or container memory limit |
mem_fragmentation_ratio | RSS vs used_memory efficiency | Above 1.5 sustained indicates fragmentation waste; below 0.8 with used_memory above 100 MB indicates swap |
evicted_keys rate | Keys being removed due to maxmemory | Sustained non-zero rate for cache workloads; any eviction for data store workloads |
rdb_last_cow_size / aof_last_cow_size | Memory consumed by COW during fork | Above 50% of used_memory indicates high OOM risk on the next fork |
total_error_replies / errorstat_OOM | Write rejections under noeviction | Any non-zero rate of OOM errors means writes are silently failing |
maxmemory value itself | Whether a limit exists at all | 0 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.
| Policy | Use case | Behavior at maxmemory |
|---|---|---|
noeviction (default) | Data store, primary database | Rejects writes with OOM error; reads continue |
allkeys-lru | Pure cache | Evicts least recently used keys |
allkeys-lfu (Redis 4.0+) | Cache with skewed access patterns | Evicts least frequently used keys |
volatile-lru | Mixed cache and persistent data | Evicts only keys with TTLs set |
volatile-ttl | Cache with expiry-based priority | Evicts keys with shortest remaining TTL first |
allkeys-lrm (Redis 8.6+) | Read-heavy cache, stale data eviction | Evicts 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):
maxmemoryup to 75% of physical RAM. Leave 25% for bursts, fragmentation, and OS overhead. - Persistent instances (RDB or AOF enabled):
used_memoryshould stay below 50% of physical RAM. This leaves headroom for COW during fork. - System RSS:
used_memory_rssshould 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 withfork: 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 REWRITEafter everyCONFIG SET maxmemory. Without it, the change is lost on restart. - Pair maxmemory with the right eviction policy.
noevictioncauses write failures when memory is full. For caches, useallkeys-lruorallkeys-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_memorywith 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_memoryandused_memory_rsscollection 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. Whenmaxmemoryis 0, the ratio is undefined, which immediately flags the misconfiguration.- COW size tracking via
rdb_last_cow_sizeandaof_last_cow_sizecorrelates 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_keysanderrorstat_OOMdistinguishes 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_ratioflags fragmentation drift before RSS silently grows past the OOM threshold, especially on instances whereused_memoryappears stable.
Netdata’s Redis monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.
Related guides
- How Redis actually works in production: a mental model for operators
- Redis NOAUTH / WRONGPASS authentication failures: ACL LOG and credential drift
- Redis aof_last_write_status:err: AOF write failures and recovery
- Redis appendfsync always latency: durability vs throughput trade-offs
- Redis big keys: finding the giant key that blocks the event loop
- Redis blocked_clients growing: dead consumers vs healthy queues
- Redis BUSY Redis is busy running a script: blocking Lua and how to recover
- Redis Can’t save in background: fork: Cannot allocate memory - diagnosis and fix
- Redis client output buffer overflow: slow consumers and client-output-buffer-limit
- Redis cluster bus port blocked: the port+10000 firewall gotcha
- Redis cluster_slots_pfail > 0: impending node failure in a cluster
- Redis CLUSTERDOWN / cluster_state:fail: slot coverage and recovery






