Troubleshooting Redis OOM: 'Maximum Memory Limit Reached OOM Command Not Allowed'

Diagnose and resolve the 'Redis maximum memory limit reached OOM command not allowed' error, preventing data writes and service interruptions. Optimize Redis memory usage and configuration for stability.


Introduction

Encountering the “Redis maximum memory limit reached OOM command not allowed” error indicates a critical issue where your Redis server has consumed all its allocated memory and is refusing write operations. This typically manifests as sudden application failures, inability to save new data to Redis (e.g., user sessions, cache entries, queue items), and a general degradation of services relying on Redis. While read operations might still function, any attempt to modify data will be met with an error, effectively crippling your application’s dynamic functionality. This guide will walk you through diagnosing the root causes and implementing robust solutions to restore stability and prevent future occurrences.

Symptom & Error Signature

When Redis hits its configured maxmemory limit and is unable to evict keys according to its maxmemory-policy, it enters an Out-Of-Memory (OOM) state. In this state, by default or with a noeviction policy, it will reject write commands.

You will typically observe this error in:

  • Application Logs: Errors reported by your application framework (PHP, Node.js, Python, Ruby on Rails) when trying to write to Redis.
  • Redis Server Logs: Direct OOM warnings from the Redis instance itself.
  • Redis CLI: Attempts to execute write commands manually will fail.

Example Application Log Output (PHP/Predis):

[2023-10-27 14:35:12] app.ERROR: Predis\ClientException: OOM command not allowed when used_memory > 'maxmemory'. in /var/www/html/vendor/predis/predis/src/Client.php:370
Stack trace:
#0 /var/www/html/vendor/predis/predis/src/Client.php(303): Predis\Client->onErrorResponse()
#1 /var/www/html/vendor/predis/predis/src/Client.php(329): Predis\Client->executeCommand()
#2 /var/www/html/app/Services/CacheService.php(80): Predis\Client->__call()
#3 /var/www/html/app/Http/Controllers/UserController.php(45): App\Services\CacheService->set()
...

Example Redis Server Log Output:

12345:M 27 Oct 2023 14:35:12.678 # OOM command not allowed when used_memory is greater than 'maxmemory' (configured as 512mb)

Example Redis CLI Interaction:

redis-cli
127.0.0.1:6379> SET mykey "hello"
(error) OOM command not allowed when used_memory > 'maxmemory'.

Root Cause Analysis

The “Redis maximum memory limit reached OOM command not allowed” error stems from a fundamental imbalance between the data stored in Redis and the memory allocated to it. The primary underlying reasons include:

  1. Insufficient maxmemory Configuration: The most common cause is that the maxmemory directive in your redis.conf is set too low for your application’s actual data storage needs. As data accumulates, Redis hits this ceiling.
  2. Ineffective maxmemory-policy: Redis’s maxmemory-policy dictates how it behaves when the maxmemory limit is reached.
    • If set to noeviction (the default for Redis without a specific policy), Redis will stop accepting write commands and return an OOM error.
    • If set to an eviction policy (e.g., allkeys-lru, volatile-lru), but there are no keys eligible for eviction, or the eviction process cannot keep up with the incoming writes, the OOM error can still occur.
  3. Application Memory Leaks or Inefficient Usage:
    • Unbounded Data Structures: Lists, Sets, or Hashes that continuously grow without proper trimming or expiration.
    • Large Keys: Storing extremely large strings, objects, or serialized data.
    • Missing Key Expirations: Data that should be temporary (e.g., cache entries, session tokens) is not given a TTL (Time-To-Live) and persists indefinitely.
    • High Churn Rates: Rapid creation of many temporary keys without efficient eviction, leading to memory pressure.
  4. Traffic Spikes: A sudden, unexpected surge in application traffic or data writes can quickly push Redis beyond its memory limits if not properly provisioned.
  5. Persistence Overhead: When Redis performs an RDB snapshot or AOF rewrite, it forks a child process. This child process shares memory with the parent via copy-on-write. If maxmemory is set very close to the total system RAM, this forking can momentarily double the memory footprint, potentially triggering system-level OOM killer or making Redis unable to persist. While this usually results in a system OOM, it can exacerbate Redis’s memory pressure.

Step-by-Step Resolution

Addressing this error requires a methodical approach, starting with diagnosis and moving towards configuration adjustments and application-level optimizations.

1. Understand Current Redis Memory Usage and Configuration

Begin by inspecting the current state of your Redis instance.

  • Connect to Redis CLI:
    redis-cli
  • Get Memory Information:
    127.0.0.1:6379> INFO memory
    Pay close attention to:
    • used_memory_human: Current memory consumption by Redis.
    • maxmemory_human: The configured maximum memory limit.
    • maxmemory_policy: The current eviction policy.
    • mem_fragmentation_ratio: Ratio of used_memory_rss to used_memory. A high ratio (>1.5) indicates fragmentation, wasting memory.
  • Check maxmemory and maxmemory-policy specifically:
    127.0.0.1:6379> CONFIG GET maxmemory
    127.0.0.1:6379> CONFIG GET maxmemory-policy
  • Examine Key Space:
    127.0.0.1:6379> INFO keyspace
    This shows the number of keys and keys with expires per database.

2. Analyze Redis Key Usage and Data Structures

Identify which keys or data structures are consuming the most memory.

  • Find Big Keys: The redis-cli --bigkeys command can help identify the largest keys in your instance.
    redis-cli --bigkeys
    This command iterates over all keys and reports the largest keys of each data type (string, list, hash, set, zset).
  • Review Application Code: This is crucial. Examine how your application interacts with Redis:
    • Are lists or hashes growing indefinitely without trimming or expiration?
    • Are large objects being stored as single string keys?
    • Are temporary cache items or sessions being created without EXPIRE times?

3. Adjust maxmemory Limit (Increase Cautiously)

If used_memory is consistently at or near maxmemory, and your system has available RAM, increasing the maxmemory limit is often the quickest temporary fix.

  • Identify Available System RAM:

    free -h

    Ensure your server has sufficient free RAM. Do not allocate all available RAM to Redis.

  • Temporarily (Runtime) Increase maxmemory:

    redis-cli CONFIG SET maxmemory 512mb # Example: Increase to 512MB

    [!WARNING] This change is not persistent. It will revert to the value in redis.conf upon Redis restart. Use this only for immediate relief while you implement a permanent solution.

  • Permanently Increase maxmemory:

    1. Edit Redis Configuration File:
      sudo nano /etc/redis/redis.conf
      # Or for a specific instance, e.g., /etc/redis/6379.conf
    2. Locate and Modify maxmemory: Find the maxmemory directive. If it’s commented out, uncomment it. Adjust the value based on your server’s available RAM and Redis’s role.
      # Old: # maxmemory <bytes>
      # New:
      maxmemory 512mb
      # Or 1gb, 2gb, etc.

      [!IMPORTANT] Memory Allocation Best Practices:

      • Do not set maxmemory to 100% of your system’s RAM. Leave adequate memory for the operating system, other system processes, and especially for Redis’s persistence mechanisms (RDB/AOF). When Redis forks a child process for persistence, memory usage can temporarily spike, potentially doubling for RDB snapshots.
      • A good starting point is often 50-70% of the total dedicated RAM for the Redis instance.
    3. Restart Redis Service:
      sudo systemctl restart redis-server
      For Dockerized Redis:
      docker restart <redis_container_name_or_id>
      Ensure your Docker Compose or docker run command for Redis includes the -maxmemory flag or links to a custom redis.conf.

4. Configure an Effective maxmemory-policy

If your maxmemory-policy is noeviction, Redis will not automatically free up space. Changing this policy allows Redis to evict keys to make room for new data.

  • Edit Redis Configuration File:
    sudo nano /etc/redis/redis.conf
  • Locate and Modify maxmemory-policy: Choose a policy that fits your use case.
    # Old: maxmemory-policy noeviction
    # New (example for general caching):
    maxmemory-policy allkeys-lru
    Common maxmemory-policy options:
    • noeviction: (Default) Returns errors on write commands when memory limit is reached.
    • allkeys-lru: Evicts the least recently used (LRU) keys among all keys. Ideal for general-purpose caching.
    • volatile-lru: Evicts LRU keys only among those that have an expire set. Useful if some keys are essential and should never be evicted.
    • allkeys-lfu: Evicts the least frequently used (LFU) keys among all keys. (Available since Redis 4.0)
    • volatile-lfu: Evicts LFU keys only among those that have an expire set.
    • allkeys-random: Evicts random keys among all keys.
    • volatile-random: Evicts random keys only among those that have an expire set.
    • volatile-ttl: Evicts keys only among those that have an expire set, preferring those with the shortest remaining TTL.

    [!IMPORTANT] Carefully consider your maxmemory-policy. If Redis is used for critical data that should not be lost (rather than just a cache), then noeviction might be appropriate, and the real solution lies in increasing maxmemory or optimizing data structures. If used as a cache, an LRU or LFU policy is usually preferred.

  • Restart Redis Service:
    sudo systemctl restart redis-server

5. Optimize Application-Level Redis Usage

This is often the most sustainable long-term solution.

  • Implement Key Expiration (TTL): For cache entries, sessions, or temporary data, always set a TTL using EXPIRE or SETEX.
    # Example in Python using redis-py
    redis_client.setex("user:session:123", 3600, "session_data_here") # Expires in 1 hour
  • Trim Growing Lists: If using lists as queues or activity streams, use LTRIM to keep them bounded.
    127.0.0.1:6379> RPUSH mylist item1 item2 item3
    127.0.0.1:6379> LTRIM mylist 0 99 # Keep only the last 100 items
  • Use Hashes for Related Data: Instead of storing many small individual keys, group related attributes into a single Hash data type. This reduces key overhead.
    # Instead of:
    # SET user:1:name "Alice"
    # SET user:1:email "[email protected]"
    # Use:
    HMSET user:1 name "Alice" email "[email protected]"
  • Avoid Storing Large Blobs: If you need to store very large objects, consider if Redis is the right tool, or if they can be stored in a file system/object storage and Redis only stores the pointer/metadata.
  • Code Review for Anti-Patterns: Look for patterns that fetch all keys (KEYS * - avoid in production!), or add data without any expiration or trimming logic.
  • Sharding or Clustering: For very high-scale applications, consider distributing your data across multiple Redis instances (sharding) or using Redis Cluster.

6. Monitor and Alert

Proactive monitoring can prevent future OOM issues.

  • Monitor Key Metrics: Use monitoring tools (e.g., Prometheus/Grafana, Datadog, New Relic) to track:
    • used_memory
    • maxmemory
    • mem_fragmentation_ratio
    • evicted_keys (indicates your eviction policy is working)
    • keyspace_hits and keyspace_misses
    • System-level RAM usage.
  • Set Up Alerts: Configure alerts for high memory usage (e.g., when used_memory exceeds 80-90% of maxmemory) to get notified before an OOM condition occurs.

7. Consider Redis Persistence Settings

If using RDB snapshots or AOF persistence, ensure these are configured thoughtfully regarding memory.

  • RDB and AOF Rewrite: As mentioned in Root Cause Analysis, these operations involve forking. Ensure maxmemory is not so close to total system RAM that a fork causes a system OOM.
  • vm.overcommit_memory: On Linux, ensure vm.overcommit_memory is set to 1 (the default in many systems) to allow the kernel to overcommit memory. This is crucial for Redis’s copy-on-write mechanism during persistence. You can check with sysctl vm.overcommit_memory and set it persistently in /etc/sysctl.conf.
    # Check current value
    sysctl vm.overcommit_memory
    
    # If not 1, set it (temporary)
    sudo sysctl vm.overcommit_memory=1
    
    # To make it persistent, edit /etc/sysctl.conf
    sudo nano /etc/sysctl.conf
    # Add or modify the line:
    vm.overcommit_memory = 1
    # Then apply changes
    sudo sysctl -p

    [!WARNING] While vm.overcommit_memory=1 is generally recommended for Redis persistence, understand that it tells the kernel to allow processes to allocate more memory than physically available. This relies on processes not actually using all that memory simultaneously. A misbehaving process could still trigger system OOM.

By systematically applying these steps, you can effectively resolve the “Redis maximum memory limit reached OOM command not allowed” error and establish a more robust, performant, and stable Redis environment.