Redis maxmemory: why a production instance crashes due to memory
maxmemory sets the ceiling from which the instance derives all subsequent logic: on every memory-allocating command, it compares the current volume against the limit and, if headroom is exhausted, switches to the eviction policy.
The complication is that the limit includes more than your data model: service structures, allocator overhead, fragmentation, and replication buffers consume a noticeable share, so the instance approaches the boundary earlier than a calculation based on key volume suggested.
Writes break first. Under a no-eviction policy, data-modifying commands start returning write errors on overflow, while reads continue to be served — the application looks alive, and some operations are silently lost if the code does not handle the failure.
That is why Redis memory overflow is found not from a monitoring chart but from data divergence between services.
Replication is affected second: a growing lag buffer, a dropped connection to the replica, and a full resync that loads the primary at exactly the moment it is already struggling.
In parallel comes response degradation under load — the fork for background save and pages swapped out stretch request processing, and client timeouts multiply in a cascade.
Hence the conclusion: the limit and policy are calculated from the access profile — which keys are cacheable, which must live until explicit deletion, and which have a TTL.
Next we break down how to measure real overhead and select eviction so that write rejection is a deliberate decision, not an outage on release night.
What a configured key eviction policy provides
An explicitly set memory limit turns storage behavior from “however it goes” into something measurable. In Redis, two settings are responsible: the maximum volume and the key eviction policy.
Until the threshold is set, consumption grows with load and hits the operating system limit — then the entire process falls, along with queues and locks, rather than individual keys.
The chosen policy turns this scenario into a manageable one: space is freed pointwise, by a pre-understood rule. We test every deployment against the same checklist.
- Stable instance memory — consumption stays within the set boundary with headroom for fragmentation and service copies; the dataset no longer grows in steps until emergency termination.
- Predictable response time — under peaks, latency grows far less: freeing memory stops being an event the user notices.
- No write failures — a whole class of “write forbidden” errors disappears when the application gets a rejection on every operation after memory exhaustion.
- Conscious eviction — keys that can truly be lost are evicted, not the data the storage exists for.
- Replica resilience — a reserve for service copies is built in advance, so load growth does not desynchronize nodes.
- Observability — evicted key counters and hit ratio show that the boundary is chosen by load, not by eye.
Options for limiting memory in Redis maxmemory
maxmemory sets the upper memory bound of the instance, but by itself it solves nothing: once the limit is reached, behavior is determined by the eviction policy.
Policy choice is essentially a choice between write rejection and partial data loss, and it must match what data in Redis means for the application.
| Option | When it fits | What it limits |
|---|---|---|
noeviction |
Cache subordinate to data: queues, counters, locks — key loss unacceptable | Writes fail with an error, reads continue; the application must degrade gracefully |
allkeys-lru / allkeys-lfu |
Pure cache, any key recoverable from the source | Evicts hot keys during a spike too — misses come in batches |
volatile-lru |
Mixed data: some keys with TTL, some persistent | Frees only keys with expiration; if their share is small, it effectively becomes noeviction |
volatile-ttl |
Sessions, temporary tokens, limits — order of survival matters | Works solely by TTL; keys without expiration do not free memory |
allkeys-random |
Uniform load without distinct hot keys | Does not account for access frequency, miss rate unpredictable |
A practical point: maxmemory is set below the container or instance limit, leaving headroom for service costs and background operations — otherwise eviction triggers later than the process runs out of memory.
Policy changes are applied on the fly, but they must be evaluated by evicted key metrics and hit ratio, not merely by the absence of errors.
The trade-off is always the same: the more aggressive the mode, the fewer write rejections and the higher the miss ratio. We break down how to tune this balance to a specific load profile.
Procedure for implementing memory limits in production
The memory limit is not a single config value but a decision that affects eviction policy, replica behavior, and failure headroom. The order below is structured so that you first understand the data profile, then set the boundary, and only then test it on live traffic.
-
Memory consumption audit. Capture used_memory, fragmentation, and daily dynamics, separately at peak load. This finds heavyweight keys that cause usage to grow faster than traffic.
-
Key and TTL profile. Break down the keyspace by prefixes: volume, count, TTL presence, what is the source of truth here and what is cache. It shows what can be evicted and what must survive until persistence.
-
Boundary calculation. Start from the profile, not from free memory on the host: leave headroom for fragmentation, background save, and replication.
Choose the policy by data class — allkeys-lru for cache, volatile-* where TTL is set deliberately, noeviction for queues and sessions.
-
Production instance configuration. Fix the value and policy in configuration, not as a one-off command: otherwise the first restart returns the previous behavior. Verify that the setting applied on all nodes, including replicas.
-
Load testing. Reproduce a scenario that pushes memory beyond the boundary and look at the evicted key count, hit ratio, and write failures. Noticeable degradation is a reason to revisit the calculation, not to clean manually after the fact.
-
Monitoring and handover of runbooks. Alerts on eviction share, fragmentation growth, and hit drop catch the wrong boundary before an incident. Together with the runbook, hand over the procedure for data growth — so the next release does not require a repeated analysis.
Configurations, schemas, and runbooks after implementation
After implementation, you get not a set of scattered commands but a kit by which your administrator or on-call engineer can reproduce the configuration from scratch, understand Redis behavior under load, and safely roll back the change.
Each artifact is versioned in the repository next to the application code — changes go through review, not made by hand on a running node.
- Redis configuration files: fixed
maxmemoryand eviction policy for the load profile, disk persistence mode, client connection limits and timeouts. - Replication and fault tolerance scheme: “primary — replicas” topology, automatic failover rules, quorum, placement across availability zones.
- Access policies and roles: separate accounts for the application, monitoring, and administrator, rights to change parameters and service commands, credential rotation.
- Dashboards and alert rules: memory fill, evicted key share, cache miss rate, replica lag, thresholds for night on-call.
- Runbook for the on-call shift: what to check when memory consumption grows, at what step to escalate, which commands are safe on a running node.
- Growth plan: criteria for moving to multiple nodes, key migration procedure, verification that the application survives a topology change.
- Rollback instructions: state snapshot, procedure for returning the previous profile, recovery drill on the test environment.
The kit covers a typical scenario: memory grows, eviction starts throwing out needed keys, the database responds slower. We show where this is caught in advance and where it is already too late.
Case: instance stopped crashing under peak load
A production Redis instance held carts, sessions, and a product catalog — and crashed every sale.
The memory limit was not set, so the process reached the host ceiling and then the system OOM killer took over: the instance restarted, the cache cooled, and requests in the first minutes after startup fell on the database.
The cost showed up not only in crashes. At peak, about 6% of writes — adding to cart, updating a promo counter — received a memory error, and some orders had to be reassembled manually.
In the night order stream, response dropped 4-5x: the request queue to the database grew faster than the instance could respond.
What changed
Data was separated by classes and eviction policies: for the catalog cache, allkeys-lru was enabled; for sessions, volatile-ttl; and critical records got a limit only by key TTL, so eviction did not touch active orders.
The memory limit was set with headroom for internal fragmentation, not at the ceiling, plus monitoring was added for the volume of evicted keys and cache hit ratio — the alert fires not on a crash but earlier.
What became
Write failures at peak — 0 instead of 6%: no order was lost. The 95th percentile response returned to calm-day level, and the 4-5x drop disappeared.
Eviction continues; during peak hours up to one-fifth of the volume is freed, but it hits catalogs and expired carts, not active orders.
The night surge stopped being a separate risk scenario: the instance handles the stream without restarts, the cache stays warm, and behavior under load growth is visible from metrics, not from the on-call engineer’s intuition.
Technical concerns about eviction and persistence
Concerns around eviction are almost always the same: will needed data disappear, will the policy break disk snapshots, can the mode be switched on a running environment. Below are short answers that most often resolve the question before work begins.
Will eviction destroy hot keys?
The policy evicts only what it is allowed to by the setting. Under volatile-*, only keys with a TTL are evicted — persistent data is not touched at all.
If the entire dataset is volatile, we mark critical keys without TTL and move them to a separate instance, and keep the evicted key share under monitoring: a spike is visible immediately, not after complaints.
Does eviction conflict with disk snapshots?
No. A snapshot captures state at the moment of writing; eviction happens after it. When the append-only journal is enabled, policy deletions also go there — recovery yields exactly the set that was in memory.
We only verify that the file write does not coincide with a load peak: separate the windows and keep disk headroom.
Can the mode be changed without downtime?
Yes. A policy change is applied on a live instance and applies to subsequent evictions. We do it during a low-load window, after taking a snapshot and running the scenario on a copy. Rollback — in the same way, in seconds, without restart or connection loss.
Let’s discuss the task and come back with an estimate and plan
To discuss the configuration task concretely, a few infrastructure inputs are enough: how maxmemory is currently set, what eviction policy is active, whether replication or a cluster exists, and what the load profile on the instance looks like.
This is enough to understand the class of task — a one-time parameter change or a revision of the storage model with keys separated by TTL.
On the call, we clarify what is not visible in the configuration: what happens on memory overflow now, what data is acceptable to lose on eviction, and what must survive the write peak.
Separately, we discuss the change window and the method for validating the result on staging before production.
We come back with a concrete set:
- target
maxmemoryvalue and justification for the eviction policy for your key profile; - analysis of failure scenarios: where data is lost and where the instance goes into write rejection instead of silent eviction;
- phased work plan with checkpoints and rollout order;
- monitoring requirements: eviction metrics, miss ratio, working set growth;
- responsibility split — what we do and what remains on your side: access, window, on-call runbook;
- acceptance checklist by which you accept the result.
Describe the task in two or three paragraphs or send the current config — we will come back with an estimate, work plan, and explicit responsibility areas.
How does TTL-based eviction differ from volume-based eviction?
maxmemory sets a hard memory ceiling, and maxmemory-policy determines what happens when it is reached. Policies fall into two classes: those evicting only keys with a TTL (volatile-lru, volatile-lfu, volatile-ttl) and those ready to take any key (allkeys-lru, allkeys-lfu). The difference between them is not in the speed of freeing memory but in what data you lose.
TTL-based eviction removes first what was due to expire anyway — keys with a TTL have the most predictable loss profile. If cache and data without a TTL live side by side in the database, volatile-* mode guarantees: under pressure, the cache goes, not the catalog or counter.
The downside is that when TTL keys run out and memory is still needed, Redis starts rejecting writes, and the application must be ready for that.
Volume-based eviction makes no distinction: any key goes under the knife, including one written without a TTL. For a pure cache, this is the simplest option — losses are invisible because the value is always restored from the source.
For queues and background jobs, this is a class of failures not cured by retry: an evicted job is not returned by anyone, and it disappears without a trace.
Therefore, queue and cache are separated into different databases or instances. For queues, set noeviction and monitor free memory; for cache — allkeys-lru with volume headroom.
Control rests on the evicted key metric and the share of keys without TTL: if it grows in the queue database, the mode was chosen incorrectly.







