Mongoose TTL indexes let MongoDB remove temporary documents without a separate cleanup worker. This guide explains how expiration works, how to choose between relative and absolute retention rules, how to use partial indexes, and how to verify that cleanup is safe in a production or cloud-native environment.
Overview
A MongoDB TTL (time to live) index is a special single-field index that causes documents to be removed after a date-based condition is met. In a Mongoose application, you define the relevant date field in the schema and configure the TTL behavior through either a path option or an explicit schema index.
TTL cleanup is performed by MongoDB's background TTL process, not by a Mongoose request or application timer. That distinction matters: expiration is automatic, but it is not an exact-time scheduling mechanism. A document can remain visible for a period after its expiration threshold while MongoDB detects and deletes it. Applications should therefore treat the expiration time as a retention boundary, not as a precise execution timestamp.
A common relative-retention model stores the time at which a document was created and removes it after a fixed number of seconds:
const sessionSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
createdAt: {
type: Date,
default: Date.now,
expires: 3600
}
});Here, Mongoose creates a TTL index using the date path and an expiration interval. The document becomes eligible for deletion approximately one hour after createdAt. For production systems, confirm the generated index rather than assuming that a schema option has been applied successfully.
An explicit index is often clearer when the retention rule is important or when you need a partial filter:
const eventSchema = new mongoose.Schema({
expiresAt: { type: Date, required: true },
temporary: { type: Boolean, default: true },
payload: mongoose.Schema.Types.Mixed
});
eventSchema.index(
{ expiresAt: 1 },
{
expireAfterSeconds: 0,
partialFilterExpression: { temporary: true }
}
);With expireAfterSeconds: 0, MongoDB uses the value in expiresAt as the expiration time. The partial filter limits TTL behavior to documents marked as temporary. This is useful when one collection contains both short-lived operational data and records that must be retained.
Checklist by scenario
Temporary sessions, tokens, and verification records
- Choose a required date field such as
expiresAtwhen each record can have a different lifetime. - Set
expireAfterSeconds: 0for an absolute expiration timestamp. - Calculate the expiration time in trusted server-side code rather than accepting an unrestricted client value.
- Assume expired records may remain queryable briefly and enforce authorization independently of TTL cleanup.
Logs, metrics, and transient event data
- Use a creation or ingestion timestamp with a fixed retention interval when all records follow the same policy.
- Check that the timestamp is stored as a BSON Date, not a formatted string.
- Estimate the volume of documents that may be removed and observe database load during cleanup.
- Keep long-term archival separate if the data may be needed for audits, investigations, or compliance workflows.
Mixed-retention collections
- Use a partial TTL index when only a defined subset should expire.
- Make the filter easy to understand, such as
temporary: trueor a controlledretentionClassvalue. - Test documents that match and do not match the filter.
- Document how a record changes state if it must be retained beyond its original lifecycle.
Cloud-native and replica-set deployments
- Verify the TTL index exists on the production database, not only in a local development environment.
- Remember that TTL deletion is database-managed and is replicated through the normal MongoDB replication process.
- Check behavior during failover, restore, scaling, and rolling deployment exercises.
- Use application metrics and database monitoring to distinguish delayed cleanup from an absent or misconfigured index.
| Retention need | Recommended model | Important check |
|---|---|---|
| Every record lives for the same duration | Timestamp plus a fixed TTL interval | The timestamp is populated consistently |
| Each record has its own deadline | Date field plus expireAfterSeconds: 0 | Deadlines are validated server-side |
| Only temporary records should expire | TTL index with a partial filter | Permanent records do not match the filter |
| Deletion must run at a precise time or follow a workflow | Scheduled job or queue-based process | Retries, auditing, and failure handling are defined |
What to double-check
Field type: TTL expiration depends on a date field. Review the stored BSON type with a representative query; values that look like dates in application code may still be strings in the database.
Index creation: Mongoose can create indexes during development, but production deployments often manage index creation separately. Inspect the collection's indexes and include index creation in a controlled deployment or migration process. The Mongoose Schema Design Guide provides useful context for treating indexes as part of schema design.
Expiration timing: Do not build user-facing guarantees around the exact second of deletion. If an expired record must be rejected immediately, check its expiration field in the request path as well as relying on TTL cleanup.
Index shape: TTL indexes are designed around a single date field. Review whether a compound query index is also needed for application reads; a TTL index should not be treated as a general replacement for query-optimization indexes.
Updates: Updating the indexed date changes the expiration schedule. Make date changes explicit and auditable, especially for security records, invitations, and recovery workflows.
Testing: Use an integration test against MongoDB to verify index creation, expiration-field writes, partial-filter behavior, and eventual removal. Unit tests alone will not prove that the database TTL monitor behaves as expected. See Testing Mongoose Models for a broader testing approach.
Common mistakes
- Expecting immediate deletion: TTL cleanup is asynchronous. Design reads and authorization so that a still-present expired document cannot be used.
- Using a string date: Serialize dates consistently and verify the stored type before diagnosing TTL behavior.
- Forgetting the index: A schema declaration does not help if the corresponding index was never created in the target database.
- Applying TTL to durable data: A mistaken field or filter can remove records that should have been retained. Test with clearly labeled fixtures and review index definitions before release.
- Using TTL as a business workflow: TTL deletion does not provide a reliable place to send notifications, call another service, or record a detailed audit event. Use a scheduled worker or queue when those actions are required.
- Ignoring backup and retention layers: Removing a document from the live collection does not automatically answer questions about backups, exports, replicas, or downstream copies. Align TTL with the wider data-retention plan.
When to revisit
Revisit a Mongoose TTL design before seasonal planning cycles, changes to retention requirements, database upgrades, or a move between deployment environments. Also review it whenever a collection changes from temporary to durable use, a new compliance or audit requirement appears, or an application begins querying expired records.
Use this practical production checklist:
- Write down what the data is, why it expires, and the maximum acceptable cleanup delay.
- Choose fixed-duration or per-document expiration.
- Confirm the field is a BSON Date and is populated on every eligible document.
- Inspect the exact TTL index and, if applicable, its partial filter expression.
- Test matching, non-matching, expired, and future-dated documents.
- Verify behavior through the same deployment path used in production.
- Add monitoring for index presence, document volume, cleanup symptoms, and application attempts to use expired data.
- Recheck the design after schema, Mongoose, MongoDB, or infrastructure changes.
When the requirement is simply “remove temporary documents eventually,” a TTL index is usually a straightforward fit. When expiration must trigger coordinated work, provide exact timing, or preserve a detailed audit trail, pair or replace it with an explicit cleanup workflow. For related schema changes and safe operational updates, consult the Mongoose Data Migration Guide and the Mongoose Performance Optimization Checklist.