Time-series data is common in applications that collect information continuously. Examples include temperature readings from sensors, application performance metrics, stock prices, equipment status, and server monitoring data. These records usually contain a timestamp, a set of measurements, and information identifying the source.

Storing this type of data in a regular MongoDB collection is possible, but time-series collections provide storage and query optimizations designed specifically for timestamped measurements.

Two important decisions when designing a time-series collection are granularity and data retention. Granularity influences how MongoDB groups measurements into internal buckets, while retention determines how long the database keeps historical data. Choosing these settings carefully can improve query performance, control storage growth, and simplify database maintenance.

This article explains how MongoDB time-series collections work, how to select appropriate granularity, and how to configure data retention for practical applications.

Understanding MongoDB Time-Series Collections

A time-series collection stores documents containing measurements associated with time. MongoDB groups related measurements into internal buckets instead of storing every measurement as an entirely independent storage unit.

For example, an industrial monitoring application might collect temperature readings every five seconds. Each reading contains a timestamp, a sensor identifier, and the measured temperature.

A document might look like this:

CSS
{
    timestamp: ISODate("2026-10-29T10:00:00Z"),
    metadata: {
        sensorId: "sensor-101",
        location: "plant-a"
    },
    temperature: 21.4,
    humidity: 43.2
}

The document contains three important categories of information:

  • timestamp identifies when the measurement was recorded.
  • metadata identifies the sensor and its location.
  • temperature and humidity contain the actual measurements.

MongoDB uses the timestamp and metadata to organize measurements into buckets. This arrangement can reduce storage overhead and improve operations involving time ranges.

Creating a Time-Series Collection

You can create a time-series collection using db.createCollection().

CSS
db.createCollection("sensorReadings", {
    timeseries: {
        timeField: "timestamp",
        metaField: "metadata",
        granularity: "seconds"
    },
    expireAfterSeconds: 60 * 60 * 24 * 30
});

Here is what each setting means:

SettingPurpose
timeFieldSpecifies the field containing the measurement timestamp.
metaFieldIdentifies the field containing metadata associated with a series.
granularitySelects a predefined time-series bucketing configuration.
expireAfterSecondsConfigures automatic expiration of measurements after the specified retention period.

The timestamp field should contain BSON Date values. The metadata field is optional, but it is useful when measurements belong to identifiable devices, sensors, locations, or other stable sources.

The retention value in this example is 30 days, expressed in seconds.

Granularity and retention solve different problems. Granularity affects how MongoDB groups measurements, while retention controls their lifecycle.

How to Choose the Right Granularity

Granularity is one of the most important configuration decisions for a time-series collection. It helps MongoDB determine appropriate bucket boundaries and grouping behavior.

MongoDB provides three predefined granularity values:

  • seconds
  • minutes
  • hours

These options represent different expected measurement frequencies. They should be selected according to the rate at which data arrives and the application's query patterns.

Seconds Granularity

Use seconds when measurements arrive frequently, such as every few seconds or every few tens of seconds.

Common examples include:

  • Industrial sensors recording machine conditions.
  • Server monitoring systems collecting CPU and memory measurements.
  • IoT devices reporting temperature, pressure, or humidity.
  • Applications recording frequent operational metrics.

For example, a sensor that records a temperature reading every five seconds may use seconds granularity.

Code
db.createCollection("fastSensorReadings", {
    timeseries: {
        timeField: "timestamp",
        metaField: "metadata",
        granularity: "seconds"
    }
});

This configuration is a reasonable starting point for frequently updated sensors. However, the actual measurement frequency, metadata consistency, query patterns, and bucket behavior should still be evaluated under realistic workloads.

Minutes Granularity

Use minutes when measurements arrive less frequently than high-frequency telemetry but still arrive regularly throughout the day.

Typical examples include:

  • Environmental monitoring systems.
  • Building management applications.
  • Periodic equipment health reports.
  • Business metrics collected at regular intervals.

For example, a monitoring application that records a reading every minute could use the following configuration:

Code
db.createCollection("environmentReadings", {
    timeseries: {
        timeField: "timestamp",
        metaField: "metadata",
        granularity: "minutes"
    }
});

This configuration is appropriate when the expected measurement interval is closer to minutes than seconds.

Do not select minutes granularity simply because application reports group measurements by minute. The frequency of incoming measurements and the collection's workload are more important than the display format used by a dashboard.

Hours Granularity

Use hours when measurements arrive relatively infrequently, such as hourly readings from devices or periodic operational reports.

Examples include:

  • Agricultural monitoring systems.
  • Long-term environmental measurements.
  • Equipment readings collected at hourly intervals.
  • Periodic business or infrastructure statistics.
Code
db.createCollection("hourlyReadings", {
    timeseries: {
        timeField: "timestamp",
        metaField: "metadata",
        granularity: "hours"
    }
});

This setting is suitable when measurements are relatively sparse over time.

If the application later starts collecting readings every few seconds, revisit the collection design rather than assuming that the existing configuration will remain optimal.

Granularity Comparison

GranularityTypical workloadExample
secondsFrequent measurementsServer metrics collected every five seconds
minutesRegular measurements at moderate intervalsEnvironmental readings collected every minute
hoursInfrequent measurementsEquipment readings collected hourly

These are practical guidelines rather than strict limits. The best choice depends on the actual arrival pattern and the queries the application needs to execute.

MongoDB versions that support custom bucket span and rounding settings may offer more control than the predefined granularity values. Those options are version-dependent, so verify the capabilities of the MongoDB server you deploy before using them.

Designing the Metadata Field

The metadata field helps MongoDB identify measurements that belong to the same series. Its design can affect how effectively the database groups measurements into buckets.

Consider a monitoring application with hundreds of sensors across several manufacturing plants.

A useful metadata structure might be:

CSS
{
    timestamp: ISODate("2026-10-29T10:00:00Z"),
    metadata: {
        sensorId: "sensor-101",
        plantId: "plant-a"
    },
    temperature: 21.4
}

The sensor and plant identifiers help describe the source of the measurement.

Keep Metadata Stable

Metadata should contain attributes that remain consistent across measurements belonging to the same series.

Good metadata fields include:

  • Sensor ID.
  • Device ID.
  • Machine ID.
  • Location ID.
  • Equipment category, when it remains stable for that series.

Avoid putting frequently changing measurement values into metadata.

For example, temperature should remain a measurement field rather than becoming part of the metadata object. If the temperature changes with every reading, using it as metadata can prevent related measurements from being grouped effectively.

Metadata design should also reflect how the application queries its data. If the application frequently retrieves readings for a particular sensor, the sensor identifier should be easy to filter.

Inserting and Querying Time-Series Data

After creating the collection, insert documents with valid timestamps and measurement values.

Insert Sample Measurements

C#
db.sensorReadings.insertMany([
    {
        timestamp: ISODate("2026-10-29T10:00:00Z"),
        metadata: {
            sensorId: "sensor-101",
            location: "plant-a"
        },
        temperature: 21.4,
        humidity: 43.2
    },
    {
        timestamp: ISODate("2026-10-29T10:00:05Z"),
        metadata: {
            sensorId: "sensor-101",
            location: "plant-a"
        },
        temperature: 21.7,
        humidity: 43.5
    },
    {
        timestamp: ISODate("2026-10-29T10:00:10Z"),
        metadata: {
            sensorId: "sensor-101",
            location: "plant-a"
        },
        temperature: 21.9,
        humidity: 43.8
    }
]);

The timestamps use UTC, which makes measurements easier to compare across devices and geographic locations.

For production applications, validate incoming timestamps and measurement values before inserting documents. Invalid timestamps or inconsistent metadata can complicate queries and data analysis.

Query a Specific Time Range

A common requirement is to retrieve measurements for one sensor during a specific period.

Bash
db.sensorReadings.find({
    "metadata.sensorId": "sensor-101",
    timestamp: {
        $gte: ISODate("2026-10-29T10:00:00Z"),
        $lt: ISODate("2026-10-29T11:00:00Z")
    }
}).sort({
    timestamp: 1
});

This query filters by sensor ID and selects measurements recorded within a one-hour interval.

Using an inclusive lower boundary with $gte and an exclusive upper boundary with $lt is useful for processing consecutive time windows. It avoids including the same boundary measurement in two adjacent intervals.

For example, one query can cover 10:00 to 11:00, while the next covers 11:00 to 12:00.

For frequently executed queries, inspect the available indexes and use MongoDB's query explain facilities to evaluate execution plans. Index options and restrictions for time-series collections vary by server version, so verify support before adding an index.

Configuring Data Retention

Time-series data can grow quickly because measurements continue arriving even when older records are rarely accessed.

Retention policies help control storage growth by automatically removing measurements that are no longer needed.

MongoDB supports expiration for time-series collections through the expireAfterSeconds collection option.

Set a Retention Period During Collection Creation

The following example configures a 30-day retention period:

CSS
db.createCollection("sensorReadings", {
    timeseries: {
        timeField: "timestamp",
        metaField: "metadata",
        granularity: "seconds"
    },
    expireAfterSeconds: 2592000
});

The value 2592000 represents 30 days in seconds.

MongoDB uses the measurement timestamps to determine expiration. The retention period is relative to the time recorded in the timeField, not simply the time when the document was inserted.

This distinction matters when devices upload measurements late. A reading uploaded today with a timestamp from several months ago may already be eligible for expiration under the configured policy.

Change Retention for an Existing Collection

Retention requirements can change as an application evolves. For example, a monitoring system might initially keep measurements for 30 days and later need to retain them for 90 days.

On MongoDB versions that support modifying time-series expiration settings through collMod, you can use a command such as:

Code
db.runCommand({
    collMod: "sensorReadings",
    expireAfterSeconds: 7776000
});

The value 7776000 represents 90 days.

Before running this command, verify that your MongoDB version supports the operation and that the collection has the expected configuration. Test retention changes in a non-production environment when possible.

Increasing retention does not recover measurements that have already been deleted. Similarly, reducing retention can make more data eligible for removal, so evaluate the impact before changing the setting.

Understand How Expiration Works

A configured retention period is not an exact deletion deadline.

MongoDB's time-series storage uses buckets containing multiple measurements. Expiration and cleanup operate with this bucket-based storage model, and background cleanup is asynchronous.

Consequently, an expired measurement may remain available for some time after its nominal expiration threshold. Do not use time-series TTL expiration as a mechanism that guarantees deletion at an exact second.

If the application has legal, contractual, or security requirements for data deletion, verify that the complete retention and deletion process satisfies those requirements. A retention setting alone should not be treated as proof of immediate or exact removal.

Granularity vs. Retention

Granularity and retention are related to time-series design, but they control different aspects of the collection.

ConsiderationGranularityRetention
Primary purposeControls the expected measurement grouping patternControls how long measurements are retained
Main inputMeasurement frequency and workloadBusiness, operational, and compliance requirements
Effect on storageInfluences bucket organization and storage efficiencyLimits how much historical data remains
Typical adjustmentReview collection design and supported bucket settingsModify the expiration configuration when supported
Main riskPoorly matched grouping behavior may reduce efficiencyData may expire earlier than intended or remain longer than expected

For example, an application can use seconds granularity while retaining data for only seven days. Another application can use hours granularity while retaining measurements for several years.

Neither setting determines the other.

Best Practices for Production Applications

Choose Granularity Before Loading Large Volumes

Estimate how frequently measurements arrive and validate the choice with representative data. Changing the collection design after substantial data has accumulated may require creating a new collection and migrating measurements.

Keep Metadata Consistent

Use stable identifiers and avoid unnecessary changes in metadata structure. Consistent metadata helps MongoDB organize related measurements more effectively.

Define Retention Based on Actual Requirements

Do not retain every measurement indefinitely by default. Consider operational needs, storage costs, analytical requirements, and applicable data-retention policies.

Different categories of measurements may need different retention periods. Separate collections can help when data classes have significantly different lifecycle requirements.

Test Queries With Realistic Data

A collection containing a few hundred records may behave differently from one containing millions of measurements across thousands of devices.

Test the queries used by dashboards, reports, and alerting systems with representative data volumes. Review query plans and indexes supported by your MongoDB version.

Monitor Late and Out-of-Order Measurements

Some devices temporarily lose connectivity and upload historical readings later. Other systems may produce measurements out of chronological order.

Validate timestamp handling and test how delayed data interacts with retention policies. Ensure that ingestion, querying, and expiration behavior match the application's requirements.

Common Mistakes to Avoid

MistakeWhy it mattersBetter approach
Choosing granularity without examining ingestion frequencyBucket organization may not fit the actual workloadMeasure typical and peak measurement intervals
Putting changing values in metadataMeasurements may be grouped less effectivelyKeep changing readings in measurement fields
Treating TTL as immediate deletionBackground expiration is asynchronousAllow for cleanup delays and validate deletion requirements
Assuming retention begins at insertion timeExpiration is based on the configured time fieldValidate timestamps and account for delayed ingestion
Ignoring index restrictionsAn unsupported index configuration may fail or be unavailableCheck the deployed MongoDB version and query plans
Using one retention policy for every data categoryDifferent data may have different lifecycle requirementsDefine retention according to business needs

Advantages and Disadvantages

Advantages

  • Time-series collections are designed for timestamped measurements and can reduce storage overhead through bucket-based organization.
  • Granularity settings help match collection behavior to the expected measurement frequency.
  • Retention policies reduce the need for manual cleanup of historical measurements.
  • Time-range queries make it practical to retrieve measurements for monitoring, reporting, and analysis.

Disadvantages

  • Granularity must be chosen with the workload in mind.
  • Time-series collections have specific restrictions that can differ from regular MongoDB collections.
  • Background expiration does not guarantee deletion at an exact moment.
  • Changes to collection design may require migration or additional operational work.

Summary

MongoDB time-series collections provide a practical way to store and query timestamped measurements from sensors, devices, and monitoring systems.

Choose seconds, minutes, or hours granularity according to the expected measurement frequency and validate the decision with realistic workloads. Use stable metadata to identify the source of each measurement, store timestamps consistently, and design queries around the application's most common time ranges.

For retention, configure expireAfterSeconds according to business requirements and understand that expiration is asynchronous. Verify version-specific behavior before changing collection settings or relying on particular index capabilities.

The main principle is to design granularity around how measurements arrive and retention around how long the application needs to keep them. Treating these as separate decisions helps create a time-series collection that remains manageable as data volume grows.