Redis in Production: What I Actually Configure
Redis is the piece of infrastructure everyone under-configures. Out of the box it's a cache with no memory limit, an eviction policy that fails writes instead of evicting, and persistence that can lose your last chunk of data. That's fine on a laptop. In production, the defaults will page you at 3am.
Here's what I actually touch on every Redis I ship, and why. None of it is exotic — that's the point.
1. maxmemory + maxmemory-policy: the two lines that matter most
Default Redis has no memory limit and noeviction. When memory fills up, writes start failing with OOM errors while reads keep working. That's the worst kind of failure: half your app looks fine, the other half is quietly dropping writes.
I always set maxmemory — below what the box actually has, leaving headroom for fragmentation and the OS — and I pick the policy based on what that Redis instance is for:
-
Pure cache:
allkeys-lru. Least-recently-used keys go first. Simple, correct for a cache. -
Sessions or mixed data with TTLs:
volatile-lru(orvolatile-ttlif you'd rather evict nearest-expiry first). -
Queue or anything you can't afford to lose:
noevictionis actually right here. I'd rather have writes fail loudly than silently drop queued jobs. But thenmaxmemorymust be sized so it never fills, and you monitor it like a hawk.
The mistake I keep seeing: one Redis doing all three jobs under one policy. Separate your concerns — or at least know exactly which data you're willing to lose.
2. Persistence: decide what "losing it" means
Redis gives you RDB snapshots, an AOF log, or nothing. The default is RDB snapshots only, which means you can lose everything since the last snapshot.
- Pure cache: persistence off is fine, and it's faster. Losing a cache is a cold start, not an incident. Write that in the runbook so nobody panics at 4am.
-
Anything you can't rebuild (sessions you don't want to nuke, queues, rate-limit state): AOF with
appendfsync everysec. You lose at most a second of writes, and the performance cost is boring. - Belt and suspenders: run both. AOF wins on restart; RDB gives you fast point-in-time copies for disaster recovery.
Whatever you choose: test the restart. I've seen more Redis "data loss" caused by nobody ever testing failover than by actual crashes.
3. Timeouts: reap the dead connections
Idle client connections pile up. Every app server that connected once and never said goodbye stays on the list. timeout 300 closes idle clients (the default is 0 — never — which is a slow leak). tcp-keepalive 300 lets the OS reap dead peers instead of holding their slots forever.
If your CLIENT LIST keeps growing week over week while traffic is flat, this is why.
4. slowlog: your free query profiler
slowlog-log-slower-than 10000
slowlog-max-len 128
Then actually read it: SLOWLOG GET. What I'm looking for isn't one slow command — it's the same command showing up hundreds of times. That's your N+1 wearing a Redis costume.
Usual suspects: KEYS * in production (use SCAN), giant hashes fetched whole instead of with HSCAN, and Lua scripts that quietly grew teeth over six months of "just one more line."
5. Key discipline: TTLs or it didn't happen
Every key gets a TTL unless you can articulate why not. Keys without expiry are how a cache becomes a memory leak with extra steps.
Here's the trap: volatile-lru and volatile-ttl only evict keys with a TTL. Keys without one are immortal — they will eventually OOM your instance no matter what policy you set. This is the single most common Redis misconfiguration I run into: policy set to volatile-lru, half the keys with no TTL, surprise OOM at 3am.
Two more habits: name keys like paths (service:entity:id) so SCAN is actually usable, and watch out for big keys. A 50MB hash is a latency bomb — on eviction, on replication, on HGETALL. Keep hashes small; split them.
6. Disable the footguns
rename-command FLUSHDB ""
rename-command FLUSHALL ""
Nobody needs to flush a production database on a whim and a redis-cli. Same treatment for KEYS and DEBUG in prod. Then the network basics: protected-mode yes, bind to the private interface only, requirepass set. Redis with no password on a reachable interface isn't a misconfiguration — it's an incident waiting for a scanner.
7. What I actually monitor
You don't need forty metrics. The ones that have ever paged me usefully:
- used_memory vs maxmemory (as a percentage — alert before it matters)
- evicted_keys rate (a spike means the working set doesn't fit, or TTLs are missing)
- instantaneous_ops_per_sec (baseline it; deviations mean something changed upstream)
-
connected_clients and blocked_clients (blocked > 0 for a long time means something's stuck on
BLPOP) - rdb_last_bgsave_status / aof_last_rewrite_status (persistence failing silently is the classic)
- keyspace hits/misses (hit rate dropping means your cache isn't caching)
8. Replication: the minimum viable setup
One replica. replica-serve-stale-data yes — serve reads during failover instead of erroring everything. If you genuinely can't afford to lose writes during a partition, min-replicas-to-write — with the honest caveat that you're trading away availability.
For most services I run, an async replica plus good monitoring beats a fancy Sentinel setup nobody will maintain. If you need real HA with automatic failover, that's a different article — and honestly, probably a managed service.
None of this is exotic. Redis in production is mostly about deciding what each instance is for, setting boundaries (memory, TTLs, timeouts), and turning off the defaults that assume a laptop. Do that, and Redis disappears into the background — which is exactly where infrastructure belongs.
Top comments (0)