Scaling Redis for High-Traffic SaaS in 2026
How to scale Redis under sustained load: eviction policies, hot keys, AOF vs RDS persistence, and clustering explained for growing SaaS teams.
How to scale Redis under sustained load: eviction policies, hot keys, AOF vs RDS persistence, and clustering explained for growing SaaS teams.
If you searched "how to scale Redis for high traffic" you're probably staring at a graph where p99 latency climbs, evictions are spiking, and memory usage keeps creeping toward maxmemory no matter how much you add. The short answer: pick an eviction policy that matches your access pattern, remove hot keys before they saturate a single shard, choose AOF or RDS persistence based on how much data loss you can tolerate on failover, and move to Redis Cluster or read replicas before a single primary becomes your bottleneck. The rest of this guide walks through each of those decisions the way we make them for client SaaS platforms handling millions of requests a day.
Redis is deceptively simple to get running and deceptively hard to run well once traffic is sustained rather than bursty. A cache that behaves perfectly at 500 requests per second can fall over at 20,000 rps for reasons that have nothing to do with your application code — memory fragmentation, a single hot key pinning one CPU core, or an AOF fsync policy that was fine in staging and catastrophic in production. This is a practical, load-tested walkthrough of the failure modes we see most often and how to design around them.
Redis is single-threaded for command execution, and it stores everything in RAM. Once used memory approaches maxmemory, the eviction policy you chose months ago starts making decisions on every write, and the wrong choice quietly corrupts your cache hit ratio long before anyone gets paged.
| Policy | Behavior | Best for |
|---|---|---|
| noeviction | Rejects writes once memory is full | Redis as a system of record, never as a pure cache |
| allkeys-lru | Evicts least recently used key across the whole keyspace | General-purpose cache, unknown or mixed access patterns |
| allkeys-lfu | Evicts least frequently used key | Skewed access patterns where popularity matters more than recency |
| volatile-lru | LRU eviction, but only among keys with a TTL set | Mixed workloads combining permanent and cached data |
| volatile-ttl | Evicts the key closest to expiring first | Session stores where near-expiry data is safest to drop |
Three configuration details matter more than which policy you pick in the abstract. First, set maxmemory-samples to 10 in production — the default of 5 makes LRU/LFU approximations noticeably less accurate under real traffic. Second, leave 20-25% memory headroom below the instance's physical RAM; Redis needs room for replication buffers, AOF rewrite buffers, and fork() copy-on-write pages during BGSAVE, and running maxmemory right up against the ceiling turns a routine snapshot into an out-of-memory kill. Third, treat evictions as a first-class metric, not a footnote — alert when evicted_keys starts climbing on a dataset that should fit comfortably, because a cache that silently evicts the wrong keys turns a normal traffic spike into a wave of database queries.
Aggregate cluster metrics can look perfectly healthy while one shard is on fire. A single celebrity product ID, a global feature-flag key, or a leaderboard key that every request touches will pin all of its traffic to one node, because Redis Cluster shards by key, not by request volume. Everything else sits idle while that one shard saturates its CPU core and latency spikes across the board, even for keys that live elsewhere.
That last point deserves emphasis: cache stampedes are one of the most common self-inflicted outages we see. If ten thousand cached entries all carry a flat 60-second TTL, they all expire together, and ten thousand requests hit PostgreSQL or MySQL simultaneously. Adding ±10% jitter to every TTL, or using a probabilistic early-refresh algorithm, spreads that load back out and is a five-minute fix for what looks like a database scaling problem.
Persistence is where teams get surprised the hardest, because it's invisible until a restart or a failover. RDS snapshots are cheap during normal operation but expensive at the moment they run: BGSAVE forks the process, and copy-on-write means a burst of write traffic during a large snapshot can spike memory usage well past what you'd budgeted. AOF (append-only file) gives you much better durability — with appendfsync everysec you lose at most one second of writes on a crash — but it adds continuous disk I/O and periodic rewrites that themselves fork the process the same way BGSAVE does.
For most production SaaS workloads we land on a hybrid: AOF enabled with appendfsync everysec for durability, combined with RDS snapshots on a longer interval for fast full restores and cross-region backup portability. If Redis is purely a cache that can be rebuilt from your primary database, disabling persistence entirely and relying on replication for availability is a legitimate and simpler choice — just be explicit about it rather than inheriting the default and finding out during an incident.
A single Redis primary with one or two read replicas will comfortably carry most SaaS products well past their first few million dollars of ARR — don't reach for Redis Cluster before you need it, since it adds real operational complexity (no cross-slot multi-key operations, trickier client configuration, resharding coordination). The signal to move is a CPU-bound primary, not a memory-bound one: if a single core is saturated on command throughput while memory still has headroom, sharding across cluster nodes is the fix; if memory is the constraint, either evict more aggressively or scale up before you scale out.
When you do cluster, keep hash tags in mind (`{user:123}:cart` and `{user:123}:session` land on the same slot) so related keys stay co-located for multi-key operations, and watch slot balance after every resharding operation — an uneven slot distribution reintroduces the exact hot-shard problem clustering was supposed to solve. Read replicas, meanwhile, are almost always worth adding early: they're cheap, they isolate read-heavy analytics or reporting traffic from the primary that's serving your application, and they give you a promotable failover target.
Using Redis for background jobs (Laravel Horizon, BullMQ) and for session storage is extremely common, and both use cases have their own scaling wrinkle: they compete with your cache traffic for the same memory and the same single-threaded command loop. A large BLPOP-based queue backlog or a burst of session writes during a traffic spike can starve cache reads of latency budget on the same instance. For any SaaS product doing more than a few thousand jobs per minute, we recommend running queues, sessions, and cache in separate logical Redis databases or, once traffic justifies it, separate physical instances — it isolates blast radius and makes capacity planning for each workload honest instead of guessed.
“The mistake we see most often isn't picking the wrong eviction policy — it's never revisiting the policy after the access pattern changes. A cache that was 90% cache-only data six months ago can become 40% session data today, and volatile-lru quietly starts behaving like noeviction on the keys that matter.”
None of this requires exotic tooling — it's disciplined defaults, applied before the traffic spike rather than during the postmortem. That's also exactly the kind of infrastructure decision that's easy to defer indefinitely inside a growing SaaS codebase, which is why teams often bring in outside AWS and Redis architecture review before a funding-driven traffic jump rather than after.
Check redis-cli --latency and INFO commandstats first. If command latency is low but your app still feels slow under load, the bottleneck is usually network round trips (too many small commands) or connection pool exhaustion, not Redis itself. Genuine Redis-side bottlenecks show up as rising evicted_keys, a saturated CPU core on one shard, or slowlog entries for specific commands.
allkeys-lru can evict any key in the dataset once memory is full, including ones without a TTL. volatile-lru only evicts keys that have an expiry set, leaving TTL-less keys untouched even under memory pressure. Use volatile-lru when Redis holds a mix of permanent data and disposable cache entries; use allkeys-lru when everything in that instance is genuinely a cache.
No — clustering shards by key, so a single hot key still lands entirely on one node and can saturate that shard's CPU even while the rest of the cluster is idle. Clustering helps with overall dataset size and aggregate throughput, but hot keys need to be split or cached client-side regardless of how many nodes you run.
It can, but at meaningful scale it's safer to isolate them into separate logical databases (SELECT 0-15) or separate instances. Queue workloads (BLPOP, XREAD) and cache workloads compete for the same single-threaded command loop and memory budget, and an unexpected job backlog shouldn't be able to degrade your cache hit latency.
AOF with appendfsync everysec gives you at most one second of data loss on a crash and is the safer default for anything holding sessions or queue state. RDS snapshots are lighter-weight and better for fast full restores or backups. Most production setups use both together; a pure cache that's fully rebuildable from your primary database can reasonably disable persistence entirely.
Running background jobs on Redis already? See how Laravel Horizon and BullMQ compare for queue throughput and reliability at scale.
Compare Horizon vs BullMQPlanning for a traffic spike or a migration to Redis Cluster? Fepiq's team designs and load-tests Redis and AWS architecture for growing SaaS products.
Talk to our teamOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
tRPC vs REST for a TypeScript SaaS backend in 2026: when end-to-end type safety wins, when you still need REST, and the hybrid pattern most production apps use.
AWS Savings Plans vs Reserved Instances for SaaS: a 2026 guide to cutting cloud costs 20-50% without locking your startup into capacity it will outgrow.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.