If Redis keys are not expiring, the problem is usually not Redis “forgetting” TTL. It is usually an application write path resetting the key, skipping EXPIRE, or assuming a key is still volatile when it is no longer.
This guide focuses on the fastest checks first: verify whether the key still has a TTL, confirm which command path writes it, and look for overwrite patterns that silently remove expiration.
Quick Answer
If Redis keys are not expiring, first check whether the key still has a TTL at all.
In many incidents, the real cause is a later SET or overwrite command that cleared the timeout, an EXPIRE path that never succeeded, or multiple writers making different assumptions about the same key.
What to Check First
Use this order first:
- inspect
TTLorPTTLon the real key - confirm whether the key shows
-1,-2, or a positive TTL - find every code path that writes the key
- check whether a later overwrite clears expiration
- inspect whether
EXPIREuses conditional flags likeNXorXX
If the TTL is already -1, you are usually debugging the write path, not Redis expiration itself.
What to check first
Start with the actual key state instead of the application code.
Run:
TTL your:key
and if needed:
PTTL your:key
A TTL of -1 means the key exists but has no expiration. A TTL of -2 means the key no longer exists.
That distinction matters because “not expiring” and “already gone” are different problems.
The most common mistake: overwriting the key
Redis documents that commands which delete or overwrite the contents of a key clear the timeout. That includes commands such as SET and GETSET.
A very common failure pattern looks like this:
- the app creates a key
- the app sets
EXPIRE - a later code path writes the key again with
SET - the TTL disappears
If your TTL suddenly becomes -1, this is the first thing to suspect.
Check whether EXPIRE is actually succeeding
EXPIRE returns 1 if the timeout was set and 0 if it was not.
That means a safe debugging path is:
SET mykey value
EXPIRE mykey 60
TTL mykey
If your application never checks the result of EXPIRE, a failed conditional mode such as XX or NX can silently leave the key without a TTL.
Watch for conditional expiration modes
Redis supports NX, XX, GT, and LT options on EXPIRE.
These are useful, but they also create easy debugging traps:
XXonly sets TTL when the key already has an expiryNXonly sets TTL when the key has no expiryGTonly sets TTL when the new expiry is greaterLTonly sets TTL when the new expiry is smaller
If a team adds one of these modes and later forgets it, the key can keep existing with no updated TTL even though the code “looks” correct.
TTL state versus write-path bug
| TTL state or pattern | What it usually means | Better next step |
|---|---|---|
TTL = -1 | Key exists without expiration | Find overwrite or missing EXPIRE path |
TTL = -2 | Key is already gone | Stop debugging expiration and check writers |
| TTL keeps returning to a large value | Another writer keeps refreshing the key | Find all refresh and cache warmer paths |
TTL disappears after SET | Overwrite cleared timeout | Use SET ... EX or restore expiry explicitly |
Verify the actual write pattern, not just one code path
Many TTL bugs happen because there is more than one writer.
Typical examples:
- a request handler sets the TTL correctly
- a background job rewrites the same key without expiration
- a cache warmer refreshes the value but not the TTL
- a session refresh path assumes the TTL survives every write
If you already know your Redis usage is getting larger over time, compare that behavior with Redis Memory Usage High. If the incident also involves disappearing keys under pressure, compare it with Redis Eviction Policy Guide.
Use the simplest reproducible test
Before blaming the full app, recreate the behavior with the exact command sequence.
Example:
SET mykey value
EXPIRE mykey 30
TTL mykey
SET mykey newvalue
TTL mykey
If the second TTL becomes -1, Redis is doing exactly what the docs describe. The fix belongs in the application write pattern, not the server.
A safer application pattern
If the value and expiry should always be created together, set them together.
For string keys, a pattern like this is safer than separate write steps:
SET mykey value EX 60
That reduces the chance that a later refactor forgets the second command.
Quick troubleshooting checklist
- confirm whether TTL is
-1,-2, or a positive value - find every code path that writes the key
- check whether
SETor another overwrite command runs afterEXPIRE - inspect whether
EXPIREusesNX,XX,GT, orLT - test the write sequence directly in Redis
If most checks point to application behavior, treat this as a write-path bug, not a Redis scheduler bug.
Symptom shortcut
- Start here if cache or session keys stay around long after they should have expired.
- If memory growth is the first visible symptom, compare with the memory guide too.
Quick commands
redis-cli TTL your:key
redis-cli PTTL your:key
redis-cli OBJECT idletime your:key
Use these to check whether the key still has expiration, whether expiry is close, and whether the key is actually being touched again.
Look for -1 TTL values, keys that keep getting their TTL reset, and idle time that never grows because another writer keeps overwriting the key.
Bottom Line
Redis usually is not forgetting TTL on its own. The more common story is that the application rewrote the key, skipped expiration, or had multiple writers with inconsistent assumptions.
In practice, start with the real TTL state, then trace every write path touching the key. Once you know where the timeout disappears, the fix is usually straightforward.
FAQ
Q. Why does TTL return -1?
Because the key exists but currently has no expiration attached to it.
Q. Does SET keep the old TTL?
Not in the common overwrite case. Redis documents that overwriting commands clear the timeout.
Q. What is the safest way to create a key with expiration?
Use a command pattern that writes the value and expiry together when possible.
Read Next
- If missing TTL is now turning into memory growth, open Redis Memory Usage High next.
- If key disappearance and memory pressure are mixing together, open Redis Eviction Policy Guide next.
Related Posts
Sources:
While AdSense review is pending, related guides are shown instead of ads.
Start Here
Continue with the core guides that pull steady search traffic.
- Middleware Troubleshooting Guide: Redis vs RabbitMQ vs Kafka A practical middleware troubleshooting guide for developers covering when to reach for Redis, RabbitMQ, or Kafka symptoms first, and which problem patterns usually belong to each tool.
- Kubernetes CrashLoopBackOff: What to Check First A practical Kubernetes CrashLoopBackOff troubleshooting guide covering startup failures, probe issues, config mistakes, and what to inspect first.
- Kafka Consumer Lag Increasing: Troubleshooting Guide A practical Kafka consumer lag troubleshooting guide covering what lag usually means, which consumer metrics to check first, and how poll timing, processing speed, and fetch patterns affect lag.
- Kafka Rebalancing Too Often: Common Causes and Fixes A practical Kafka troubleshooting guide covering why consumer groups rebalance too often, what poll timing and group protocol settings matter, and how to stop rebalances from interrupting useful work.
- Docker Container Keeps Restarting: What to Check First A practical Docker restart-loop troubleshooting guide covering exit codes, command failures, environment mistakes, health checks, and what to inspect first.
While AdSense review is pending, related guides are shown instead of ads.