Operations
Redis & caching

Choose the Right Redis maxmemory-policy for Django

Pick Redis maxmemory and maxmemory-policy settings for Django workloads: allkeys-lru or allkeys-lfu for caches, noeviction for Celery brokers and locks, volatile policies for mixed use, and how to split instances so evictions never drop queued tasks or sessions.

Problem statement

Redis keeps everything in memory. When it reaches maxmemory, the maxmemory-policy decides what happens: evict keys, or refuse writes. The wrong choice hurts Django in opposite ways. A cache with noeviction starts failing writes with OOM command not allowed, while a Celery broker with allkeys-lru silently deletes queued tasks. Many projects run cache, sessions, and Celery on one Redis with the default settings and discover the problem in production.

Redis at its memory limitmaxmemory reachedused = limitPolicy decidesevict or refuseCache: evict LRUsafe to loseBroker: refusenever drop tasksThe right policy depends on whether the data can be lost.
  1. Always set maxmemory explicitly (leave headroom below the server or container limit).
  2. Pure Django cache: maxmemory-policy allkeys-lru (or allkeys-lfu for skewed access).
  3. Celery broker, Celery results you rely on, locks, rate limiters with no TTL: noeviction.
  4. Sessions in Redis: volatile-lru with session TTLs, or better, a separate instance with noeviction and enough memory.
  5. If roles share one instance, split them; one policy cannot be right for all of them.

Step-by-step solution

1. Inventory what lives in Redis

redis-cli INFO keyspace
redis-cli --scan --pattern '*' | head -50
redis-cli MEMORY USAGE some:key

Typical Django keys: cache entries (:1:views.decorators...), sessions (django.contrib.sessions.cache...), Celery queues (celery, _kombu.binding.*), Celery results (celery-task-meta-*), Channels groups, and rate-limit counters. Note which ones must never disappear.

2. Configure a cache instance

# redis-cache.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
maxmemory-samples 10
save ""
appendonly no

allkeys-lru evicts the least recently used keys among all keys, which is exactly what a cache should do. allkeys-lfu evicts the least frequently used, which keeps popular items better when access is very skewed. Without persistence, restarts are instant and the cache simply refills.

policy and where it fitsEvictsUse forallkeys-lruleast recently used, any keypage and query cacheallkeys-lfuleast frequently usedskewed hot keysvolatile-lruLRU among keys with TTLmixed, with carevolatile-ttlshortest TTL firstrarelynoeviction!nothing; writes failCelery broker, locksEvicting policies suit caches; noeviction suits data you cannot losedjango-deployment.com
Redis default is noeviction, which is wrong for a dedicated cache.

3. Configure a broker instance

# redis-broker.conf
maxmemory 1gb
maxmemory-policy noeviction
appendonly yes
appendfsync everysec

With noeviction, when memory is full Redis rejects writes with OOM command not allowed, so .delay() fails loudly instead of tasks vanishing. Alert at 70 to 80 percent memory, and keep queues short by scaling workers. Set CELERY_RESULT_EXPIRES so results do not fill the broker.

4. Handle sessions and mixed instances

With volatile-* policies, only keys with a TTL can be evicted. Django's cache-based sessions have TTLs, so volatile-lru would evict sessions (logging users out) before refusing writes. If you must share one instance for cache and Celery, volatile-lru protects Celery queues (which have no TTL) while letting cache entries go, but cache entries set with timeout=None become unevictable. A dedicated cache instance is simpler and safer.

5. Monitor evictions and memory

redis-cli INFO stats | grep -E "evicted_keys|keyspace_hits|keyspace_misses"
redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio"

Rising evicted_keys on a cache instance means the cache is too small for its working set; hit ratio tells you whether that matters. Any eviction on a broker instance is a configuration error.

one instance or several?What does Redis hold?cache onlyOne instanceallkeys-lrucache + CeleryTwo instanceslru + noevictionmust share onevolatile-lruTTLs on all cache keysSeparate instances let each role have the right policydjango-deployment.com
Managed services make a second small instance cheap compared with an outage.

Explanation

When used_memory exceeds maxmemory, Redis runs the configured policy on each write. Eviction policies sample keys and remove those that best match the criterion (least recently used, least frequently used, or nearest expiry), among all keys or only keys with a TTL. noeviction refuses commands that would increase memory. Django's cache is designed to tolerate missing keys; Celery's broker and locks are not. Matching policy to data durability is the whole decision.

Edge cases / notes

  • Container limits. Set maxmemory below the container memory limit, leaving room for fragmentation, replication buffers, and fork during persistence.
  • Managed services. ElastiCache, Memorystore, and Azure set policies through parameter groups; defaults vary by provider.
  • Big values. A few large cached values can push out many small useful ones; cap what you cache.

Next steps

FAQ

What is the default maxmemory-policy?

noeviction. It is safe for brokers but makes a dedicated cache fail writes when full.

Which policy should a Django cache use?

allkeys-lru in most cases, or allkeys-lfu when a few keys are much hotter than the rest.

Can eviction delete Celery tasks?

Yes, with allkeys-* policies. Use noeviction on the broker instance.

Why are users logged out when Redis is full?

Cache-based sessions have TTLs and are evicted under volatile-* or allkeys-* policies. Give sessions their own instance or use cached_db sessions.

Keep going