Set Keys With TTL
Learn to set keys with TTL expiration in Redis. Understand time-to-live discipline, hands-on exercise, and when to choose expiration over other key management strategies.
Focus: set keys with ttl expiration
When you store ephemeral data in Redis — session tokens, rate-limit counters, or cached API responses — every key that lives forever becomes a liability. Left unchecked, stale keys bloat memory, degrade eviction behavior, and stack up as technical debt in your cache layer. Setting keys with a time-to-live (TTL) expiration is a fundamental Redis discipline. Without it, you're not building a cache — you're building a memory leak.
The problem this lesson solves
Redis is an in-memory data store. Every key consumes RAM, and RAM is finite. If you never expire keys, memory fills up, performance degrades, and Redis may start evicting your most important data via the configured eviction policy. For cache workloads like session stores or temporary locks, keys should self-destruct after a known lifetime. The problem is: how do you set, inspect, and manage that lifetime reliably? This lesson fills that gap, giving you the exact commands and patterns to set keys with TTL expiration, check remaining time, and avoid common pitfalls.
Core concept / mental model
Think of TTL (time-to-live) as a countdown timer attached to every key Redis supports — strings, hashes, lists, sets, sorted sets, and streams. When you set a key with expiration, you tell Redis: “Delete this key automatically after N seconds.” The mental model is like a parking meter: you pay for a certain time, and once the time runs out, the spot is freed without any manual action.
Pro tip: TTL is stored as an integer representing seconds (or milliseconds). After the timer expires, the key is removed lazily on access or actively by Redis's background expiration cycle.
Key definitions:
- TTL: Time To Live — remaining time in seconds.
- PTTL: Time To Live in milliseconds (more precise).
- Expire: The point in Unix timestamp when the key will be purged.
Two approaches to set expiration
Redis gives you two clean ways to associate TTL with a key:
- On creation — use
SET key value EX secondsorSETEX key seconds value. - After creation — use
EXPIRE key secondsorPEXPIRE key millisecondson an existing key.
Both approaches achieve the same end state. The choice depends on whether you want to combine set-and-expire in one atomic call (which is generally preferred) or adjust TTL on keys that already exist.
How it works step by step
Follow the logical flow Redis executes when you set a key with TTL:
- You issue a SET command with an expiration flag (e.g.,
SET session:abc "data" EX 300). - Redis stores the key's value in memory and records the absolute expire time (current Unix time + 300 seconds).
- A background timer runs every 100ms. On each cycle, Redis samples a subset of keys with expiry, deletes any whose expire time has passed, and records stats.
- When any command touches a key with expired TTL, Redis checks and deletes it proactively (lazy deletion).
- You can inspect remaining TTL with
TTL key(returns -2 if key doesn't exist, -1 if no expiry). - You can remove the expiry entirely with
PERSIST key(makes it permanent). - You can update the TTL at any time with another
EXPIREcall — this overwrites the previous timer.
Note: TTL is not for
SETalone. Every Redis data type —HSET,LPUSH,SADD,ZADD— can be expired with the sameEXPIREcommand after creation.
Atomic commands cheat sheet
| Command shortcut | Equivalent to |
|---|---|
SET key value EX seconds |
SET key value then EXPIRE key seconds (but atomic) |
SETEX key seconds value |
SET key value EX seconds (older alias) |
PSETEX key ms value |
SET key value PX milliseconds |
EXPIRE key seconds |
Set TTL on existing key in seconds |
PEXPIRE key ms |
Set TTL on existing key in milliseconds |
EXPIREAT key unix |
Set expiry at a specific Unix timestamp (seconds) |
PEXPIREAT key unix_ms |
Set expiry at a specific Unix timestamp (ms) |
Hands-on walkthrough
Let's set keys with TTL expiration in a real workflow. Open your Redis CLI (redis-cli) and follow along.
Example 1: Set and expire in one command
127.0.0.1:6379> SET cache:weather:london "sunny, 22°C" EX 60
OK
127.0.0.1:6379> TTL cache:weather:london
(integer) 57
Expected output: TTL counts down from 60. After 60 seconds, GET cache:weather:london returns (nil).
Example 2: Set TTL on an existing key
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Create a session key
r.hset('session:abc', mapping={'user': 'alice', 'role': 'admin'})
# Set TTL to 3600 seconds (1 hour)
r.expire('session:abc', 3600)
# Check remaining time
ttl = r.ttl('session:abc')
print(f"Session expires in {ttl} seconds")
# Output: Session expires in 3599 seconds
Output: The remaining time will be close to 3600 (maybe 3599 after the command).
Example 3: Set TTL with milliseconds precision
127.0.0.1:6379> SETEX rate:limit:ip:192.168.1.1 10 "5"
OK
127.0.0.1:6379> PTTL rate:limit:ip:192.168.1.1
(integer) 9882
Here SETEX is the classic Redis command (now redundant with SET ... EX). PTTL returns milliseconds — useful for high-precision caching.
Example 4: Removing expiry
# Make a key permanent
r.persist('session:abc')
ttl = r.ttl('session:abc')
print(f"TTL after persist: {ttl}") # Output: TTL after persist: -1
-1 means the key has no expiry.
Compare options / when to choose what
| Scenario | Recommended approach | Why |
|---|---|---|
| Set a new cache entry with a lifetime | SET key value EX seconds |
Atomic, one round-trip, no extra commands |
| Add expiry to an existing key (no overwrite) | EXPIRE key seconds |
Works on any data type after creation |
| High-precision TTL (sub-second) | SET key value PX milliseconds |
Avoids rounding errors from seconds |
| Set expiry at a specific calendar time | EXPIREAT key unix_timestamp |
Handy for daily midnight expiry or coupon expiration |
| Remove TTL from a key (make permanent) | PERSIST key |
Reverts to no expiry |
| Inspect remaining TTL (for debugging) | TTL key / PTTL key |
Returns -2 if key gone, -1 if no expiry |
Key insight: Prefer SET ... EX over SETEX for new Redis code — it's more explicit and symmetrical with PX. For retrofitting TTL onto existing keys, EXPIRE is the only option.
Troubleshooting & edge cases
- TTL returns -2: Key doesn't exist or already expired. Your
EXPIREcall may have been too late — or the key was deleted by another client. - TTL returns -1: Key exists but has no expiry. You forgot to set TTL, or you called
PERSISTinadvertently. - Setting TTL on a key that already has TTL:
EXPIREoverwrites the previous timer. There's noEXTENDcommand — you must calculate new total. - Using
SETwithoutEXorPX: Key becomes permanent. If you need expiry, always pass the flag. - Expiry on complex data types: Works exactly the same. But if you modify a list or hash after setting TTL, the original TTL remains — it does not reset like a sliding window.
- Sliding expiration: Redis does not natively support sliding TTL (reset on each read). You'd need to call
EXPIREinside your application logic on every access. - Large TTL values: Max TTL is 9223372036854775807 seconds (technically near-infinite). Practically, anything above 2^31 seconds works fine.
Common mistake example:
127.0.0.1:6379> SET key "hello" EX 10
OK
127.0.0.1:6379> TTL key
(integer) 8
127.0.0.1:6379> GET key
"hello"
# Wait 10+ seconds...
127.0.0.1:6379> GET key
(nil)
127.0.0.1:6379> TTL key
(integer) -2
Another edge case: Using EXPIRE with a non-positive value (e.g., EXPIRE key 0) deletes the key immediately — that's a bug trap.
What you learned & what's next
You now understand how to set keys with TTL expiration — the single most important mechanism for cache invalidation and key lifecycle management in Redis. You can:
- Explain the core idea behind TTL and how Redis manages expiration internally.
- Apply
SET ... EX,EXPIRE,PERSIST, andTTLin both CLI and Python. - Choose between creation-time expiry and post-hoc expiry based on your use case.
- Troubleshoot common TTL pitfalls like -1 vs -2 returns and sliding expiration limitations.
Now you're ready to combine TTL with more advanced patterns. The next logical step is key space scanning with SCAN and expiry propagation — understanding how to efficiently inspect all keys (including those with TTL) without blocking production traffic. Move on to the next lesson to master key scanning.
Practice recap
Open your Redis CLI. Create a hash called user:profile with fields name and email. Set its TTL to 60 seconds. After 30 seconds, check TTL and verify it's decreasing. After 60 seconds, confirm the key is gone with EXISTS. Then remove the TTL before it expires using PERSIST, and verify with TTL.
Common mistakes
- Forgetting to pass EX or PX when calling SET — key becomes permanent and memory fills up.
- Trying to read TTL before the key is fully created — returns -2, causing confusion if you don't check existence first.
- Assuming EXPIRE on a key with existing TTL adds time instead of overwriting — it always overwrites.
- Using EXPIREAT with a past Unix timestamp — key gets deleted instantly (TTL becomes -2).
Variations
- Alternative API:
EXPIREATandPEXPIREATfor setting expiry at an absolute Unix timestamp rather than a relative TTL. - For Redis 6.0+:
SET ... EXATflag to set an exact Unix timestamp directly in the SET command. - Tombstone patterns: Instead of letting keys expire naturally, you can explicitly delete them with
DEL— but TTL is preferred for automated cleanup.
Real-world use cases
- Session token TTL (e.g., 1 hour after creation) — expire automatically to force re-login.
- Rate-limiting counters with TTL (e.g., 10 requests per minute) — key self-destructs after the window closes.
- Cached API responses (e.g., weather or pricing data) — TTL set to 5 minutes; stale data disappears without manual cleanup.
Key takeaways
- TTL is a countdown timer — set it at key creation with SET ... EX/EX or after with EXPIRE.
- Use PTTL for millisecond precision and EXPIREAT for absolute Unix timestamp expiry.
- TTL returns -1 for permanent keys and -2 for non-existent or already expired keys.
- EXPIRE on a key with TTL overwrites the previous timer — it never extends.
- Redis does not provide built-in sliding TTL — implement it in application code if needed.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.