Your Redis TTL was one hour. The key is still there a day later.

Expired Redis keys are not always deleted on the second they expire. Lazy expiration, SET without EX, and writers that refresh the key explain most surprises.

Your Redis TTL was one hour. The key is still there a day later.

You set EX 3600.

Yesterday.

The key is still in Redis.

Most people assume Redis deletes a key the moment it expires. That is not how it works.

Expiration is a policy, not a clock striking midnight

Redis expires keys in two ways:

Lazy expiration. The key is removed when someone tries to read it and Redis notices it is past due. Until then, it can sit in memory.

Active expiration. Redis periodically samples keys that have a TTL and deletes the ones that have expired. It does not scan the entire keyspace every millisecond.

So an expired key can linger. That is expected, not a Redis bug.

The more common bug: you removed the TTL

Expiration is attached to the key, not to the value.

This command wipes the TTL:

await redis.set("session:42", payload);

This one sets (or resets) it:

await redis.set("session:42", payload, "EX", 3600);

A background job that “updates the session” with a plain SET will keep the data forever, even if the first write had a one-hour TTL.

A third case: something writes the key again just before it dies, with a fresh expiry. The timer never reaches zero.

What to check

If a key outlives its TTL, do not start with “Redis is broken.”

Ask:

- Did a later SET drop the expiry?
- Is a writer refreshing the key on a loop?
- Has anyone actually *read* the key since it expired (lazy delete)?

Takeaway

TTL is not a guarantee that memory is empty at now + 3600.

It is a contract on the key. Plain writes break that contract. Lazy expiration delays the cleanup.

When a key “won’t die,” inspect the write path before you inspect Redis.