---
title: "Redis"
description: "Every new Redis key sets a TTL or is registered as accepted durable data. What that means in practice, and which commands silently drop a TTL."
url: https://develop.sentry.dev/backend/application-domains/redis/
---

# Redis | Sentry Docs

This document uses key words such as "MUST", "SHOULD", and "MAY" as defined in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) to indicate requirement levels.

Sentry uses Redis for caches, counters, buffers, and other short-lived state. Redis holds everything in memory, and our capacity planning and cluster migrations assume that keys expire. A key that lives forever quietly turns a cache into a datastore: memory grows without bound, and the cluster can no longer be drained, resharded, or failed over without data loss, or more complex migration operations.

## [Every New Key Gets a TTL](https://develop.sentry.dev/backend/application-domains/redis.md#every-new-key-gets-a-ttl)

Every new Redis key **MUST** have a TTL.

If the data genuinely cannot expire, you **MUST** instead register the key with Infrastructure Engineering as accepted durable data: what the key holds, why it cannot expire, and how to rebuild it if the cluster loses it. Infrastructure Engineering maintains the list of accepted durable keys.

## [A TTL Makes a Key Evictable](https://develop.sentry.dev/backend/application-domains/redis.md#a-ttl-makes-a-key-evictable)

A TTL is an upper bound on how long a key lives, not a promise that it lives that long. When a cluster reaches its memory limit, Redis evicts keys according to the cluster's `maxmemory-policy`.

Memorystore for Valkey defaults to `volatile-lru`, and a cluster that does not set `maxmemory-policy` runs that default. Under any `volatile-*` policy, only keys with a TTL can be evicted. A key with no TTL is never evicted, while a key with a TTL can be dropped under memory pressure long before its TTL runs out. Adding a TTL to a key moves it into the pool of keys the cluster is allowed to throw away.

For caches and other data you can rebuild, that is fine, as long as the code that reads the key treats a missing key as a normal outcome. For data that must not disappear, it is backwards:

* You must not add a TTL to durable data just to satisfy the rule above. Register the key as accepted durable data instead.
* If the data must expire eventually but must not disappear before then (for example, it must be kept for exactly 30 days), a TTL alone does not guarantee that on a `volatile-*` cluster.

## [A Sliding TTL Is Not a Bound](https://develop.sentry.dev/backend/application-domains/redis.md#a-sliding-ttl-is-not-a-bound)

Calling `EXPIRE` every time you write to a key does not bound its lifetime. It resets the clock: as long as writes keep arriving, the key never expires, and neither does the data it already holds. A set you refresh on every append still holds its very first member years later.

If a continuously-written key needs a real bound, bound the data instead. Shard by time window (`mykey:2026-08-27`) and give each shard a fixed TTL, so old shards age out on their own while new writes go to new shards.

## [Keeping a TTL Is Its Own Job](https://develop.sentry.dev/backend/application-domains/redis.md#keeping-a-ttl-is-its-own-job)

Some write commands silently discard the TTL a key already has:

* `SET` and `GETSET` replace the value and drop the TTL. Pass `KEEPTTL` to `SET` if you mean to keep it.
* `SETEX` throws away the existing TTL and installs the one you pass.

Others leave the TTL alone: `SADD`, `ZADD`, `HSET`, `HINCRBY`, and `INCR` modify the value without touching the expiry.

None of them ever adds a TTL either. A key created through one of these commands has no expiry at all until something calls `EXPIRE` on it.

This distinction is where leaks come from: code that sets the TTL correctly in one place, then loses it through a later write to the same key. Reviewing a change by asking "does this write set an expiry?" will not catch that. The question that catches it is "does any write to this key drop one?"
