Using MongoDB TTL Indexes to Automatically Expire Temporary Data
Learn how to set up a MongoDB TTL index to automatically delete temporary data after a configurable interval, with a worked example, limits, and verification steps.
24 Oct 2025, 00:22 UTC

Quick answer
To have MongoDB remove documents automatically after a defined period, create a single-field TTL index on a BSON Date field and specify expireAfterSeconds. The background TTL thread runs roughly every 60 seconds and deletes any document whose indexed date plus the TTL value is in the past.
Worked example: expiring log entries after one day
Assume you store application logs in a collection called appLogs. Each log document should contain a field loggedAt that holds the timestamp when the log was written.
- Ensure you have the proper privileges – you need the
readWriterole on the database (or higher) to create indexes and insert documents. - Create the TTL index (run in the mongo shell or via a driver):
The command creates a single-field ascending index. If an index on the same key already exists with different options, MongoDB will return an error; you must drop the existing index first.use myAppDB // Index on the loggedAt field, expire after 86400 seconds (1 day) db.appLogs.createIndex( { "loggedAt": 1 }, { expireAfterSeconds: 86400 } ) - Insert a test document that should expire immediately – set
loggedAtto a timestamp in the past (e.g., two days ago).
Because the TTL is 1 day, this document is already eligible for deletion.const twoDaysAgo = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000) db.appLogs.insertOne({ message: "test expiry", loggedAt: twoDaysAgo }) - Wait for the background TTL thread – the thread wakes up approximately every 60 seconds. In practice you may need to wait up to that interval plus a few seconds for the deletion to occur.
- Verify removal – check the document count before and after the wait.
If the count drops to zero, the TTL index is working as expected.// Before waiting (should be 1) db.appLogs.countDocuments({ message: "test expiry" }) // After waiting up to 70 seconds, run again (should be 0) db.appLogs.countDocuments({ message: "test expiry" })
How the mechanism works
When a TTL index is created, MongoDB stores the expireAfterSeconds value as part of the index metadata. The internal TTL monitor scans the index roughly every 60 seconds, computes the expiry moment as indexedDate + expireAfterSeconds, and removes any document where that moment is earlier than the current time. Deletions are performed as regular delete operations, so they generate oplog entries and replicate to secondaries like normal writes.
Limits and constraints
- Single-field only – a TTL index cannot be compound. Attempting to add another field (e.g.,
{ loggedAt: 1, level: 1 }) withexpireAfterSecondswill produce an error. - Field must be BSON Date – using a string, integer, or other non-date type for the indexed field disables TTL behavior; the document will never be auto-removed.
- Missing or null field – if a document lacks the indexed date field or the field is
null, the document is excluded from TTL consideration and will persist indefinitely. - Not compatible with capped collections – TTL indexes cannot be created on capped collections because capped collections have their own size-based eviction policy.
- Deletion lag – because the background thread runs periodically, actual removal may lag up to about 60 seconds (plus any replication delay). Do not rely on TTL for immediate cleanup.
Common mistakes and how to avoid them
- Using the wrong data type – developers sometimes store timestamps as ISO-8601 strings (
"2026-09-01T12:00:00Z"). Insert a properDateobject; otherwise TTL will not fire. - Forgetting
expireAfterSeconds– the index creation command must include the options document. Without it, MongoDB creates a normal ascending index and no automatic deletion occurs. - Expecting instant deletion – after inserting a document with a past date, waiting only a few seconds may still show the document. Verify after at least one TTL tick (about 60 s) and confirm the index options with
db.collection.getIndexes(). - Applying TTL to a capped collection – MongoDB will reject the operation. Either use a non-capped collection or handle expiration at the application level.
- Accidentally indexing a non-date field – if you mistakenly index an integer counter, the TTL monitor ignores it, leading to surprise data retention. Review index definitions with
db.collection.getIndexes()and look for theexpireAfterSecondsfield.
Practical verification checklist
- Confirm the index exists and includes
expireAfterSeconds:db.appLogs.getIndexes() // Look for: "expireAfterSeconds" : 86400 - Insert a document with a known past date and note its
_id. - Wait at least 70 seconds (to cover one TTL tick plus a small buffer).
- Run a count or find query for the inserted
_id. It should return zero results. - Optionally, compare
db.appLogs.countDocuments({})before and after the wait to observe the removal.
Rollback considerations
Creating a TTL index modifies the collection's index set, which is a state change. If you need to revert, drop the index:
db.appLogs.dropIndex({ "loggedAt": 1 })
Dropping the index stops further automatic deletions but does not restore documents that have already been removed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.