TTL Indexes in MongoDB: Automate Session Cleanup Without Cron Jobs
Learn how MongoDB TTL indexes can automatically purge stale sessions, the background expiry thread, and the trade‑offs versus manual jobs.
16 Jul 2026, 05:19 UTC

Problem: Manual Session Expiration
In many web applications, user sessions are stored in MongoDB for persistence. Developers often write cron jobs or application‑side code that periodically scans for sessions older than a threshold and deletes them. This adds operational complexity, introduces a new scheduled task, and can miss edge cases if the job fails or the server is under load.
How TTL Indexes Work
A TTL (time‑to‑live) index tells MongoDB to automatically delete documents whose indexed date field is older than a configured number of seconds. The index is created on a single date field with the expireAfterSeconds option:
db.sessions.createIndex({ lastTouchedAt: 1 }, { expireAfterSeconds: 3600 })
MongoDB runs a background thread called the TTL monitor that wakes every ~60 seconds (exact interval is implementation‑dependent) and deletes qualifying documents. The deletion is performed as a normal delete operation, so it is replicated to secondaries and logged in the oplog.
Key constraints:
- Only a single‑field index is allowed.
- Indexed field must contain valid BSON
datevalues. - Documents missing the field or with a non‑date value are never expired.
- TTL deletions are approximate; a document can survive up to a minute or more after its expiry time.
Worked Example: One‑Hour Session Expiry
Suppose we want sessions to expire one hour after the last activity. The following steps illustrate the full workflow on a standalone server or primary of a replica set.
- Create the collection and insert a test session.
use appdb // Insert a session that was last touched 2 hours ago const oldSession = { _id: ObjectId("64b2f2a1e1d3c9b1f0a5c123"), userId: ObjectId("64b2f2a1e1d3c9b1f0a5c456"), lastTouchedAt: new Date(Date.now() - 2 * 60 * 60 * 1000), data: { /* session data */ } } db.sessions.insertOne(oldSession) - Define the TTL index.
db.sessions.createIndex({ lastTouchedAt: 1 }, { expireAfterSeconds: 3600 })Verify the index definition:
db.sessions.getIndexes().forEach(i => printjson(i)) - Wait for the TTL monitor. In a test environment, the document should disappear within a minute or two. Check the collection after 70 seconds:
db.sessions.find({ _id: ObjectId("64b2f2a1e1d3c9b1f0a5c123") })
It should return an empty cursor, indicating the document was deleted.
Trade‑offs & Limitations
While TTL indexes eliminate the need for scheduled cleanup code, they come with trade‑offs:
| Aspect | TTL Index | Manual Job |
|---|---|---|
| Determinism | Approximate (±1 min) | Exact |
| Control | Limited (cannot batch, cannot log per‑document) | Full (custom logic, batching, audit logs) |
| Observability | Only via server logs and db.system.profile | Custom metrics, alerts |
| Replication Impact | Deletes replicate as normal ops; can increase oplog traffic | Depends on job design |
| Per‑document deadlines | Use expireAfterSeconds: 0 and store absolute expiry date | Custom field logic |
| Field presence | Missing or non‑date values are ignored silently | Explicit checks |
On busy replica sets, the TTL monitor can become a source of replication lag because delete operations are written to the oplog and replayed on secondaries. Monitoring rs.printReplicationInfo() and reviewing the oplog size helps detect this.
Take‑away & Next Steps
If your application can tolerate an approximate expiry window and you want to reduce operational overhead, a TTL index is a clean solution. To adopt it:
- Ensure the indexed field is always a
dateand populated. - Use
expireAfterSeconds: 0for per‑document absolute expiries. - Monitor the TTL monitor via
system.profileor server logs to confirm deletions. - For compliance or audit requirements, supplement TTL with a lightweight audit trail or a secondary job that logs deletions.
When precise timing or advanced cleanup logic is required, a scheduled job remains the better choice. In many cases, a hybrid approach—TTL for bulk expiration and a job for edge cases—offers the best of both worlds.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.