Introduction

Cosmos DB Time-to-Live (TTL) automatically deletes documents after a specified time period. When TTL doesn't work as expected, documents remain in the container indefinitely, increasing storage costs and potentially exposing stale data.

Symptoms

Documents not expiring:

```sql -- Documents with old timestamps still exist SELECT * FROM c WHERE c._ts < 1700000000

-- Documents should have been deleted but still present SELECT * FROM c WHERE c.ttl = 3600 AND c.lastModified < '2024-01-01' ```

TTL property ignored:

bash
# Documents have TTL property but aren't being deleted
# _ts is old, ttl is set, but document still exists

Container TTL not working:

bash
# Container has default TTL set
# But documents aren't being cleaned up

Common Causes

  1. 1.TTL not enabled on container - Default TTL not configured
  2. 2.Document missing TTL property - No per-document TTL override
  3. 3.Timestamp property missing - Custom timestamp property doesn't exist
  4. 4.TTL value too large - TTL set to -1 (never expire)
  5. 5.Time unit mismatch - Using milliseconds instead of seconds
  6. 6.No indexing on TTL - TTL path not indexed
  7. 7.Background process delay - TTL cleanup can take time

Step-by-Step Fix

  1. 1.Check logs for specific error messages
  2. 2.Verify configuration settings
  3. 3.Test network connectivity
  4. 4.Review recent changes
  5. 5.Apply corrective action
  6. 6.Verify the fix

Step 1: Check Container TTL Configuration

```bash # Get container default TTL az cosmosdb sql container show \ --account-name my-cosmos \ --resource-group my-rg \ --database-name my-db \ --name my-container \ --query '{DefaultTtl:resource.defaultTtl}'

# -1 = TTL disabled (documents never expire) # 0 = TTL disabled # > 0 = TTL enabled, default seconds ```

Step 2: Enable TTL on Container

```bash # Enable TTL with default of 1 hour (3600 seconds) az cosmosdb sql container update \ --account-name my-cosmos \ --resource-group my-rg \ --database-name my-db \ --name my-container \ --ttl 3600

# Disable TTL az cosmosdb sql container update \ --account-name my-cosmos \ --resource-group my-rg \ --database-name my-db \ --name my-container \ --ttl -1 ```

Step 3: Check Document TTL Property

```sql -- Check if documents have TTL property SELECT c.id, c.ttl, c._ts FROM c

-- TTL property must be a number (seconds) -- If missing, container default TTL applies -- If -1, document never expires (override) -- If null or missing, container default applies

-- Example: Document with 1 hour TTL { "id": "doc1", "ttl": 3600, "data": "..." } ```

Step 4: Add TTL Property to Documents

```csharp // Add TTL when creating documents var document = new { id = "doc1", data = "some data", ttl = 3600 // Expire in 1 hour };

await container.CreateItemAsync(document, new PartitionKey(document.id)); ```

sql
-- Update existing documents to add TTL
UPDATE c SET c.ttl = 3600 WHERE c.category = 'temporary'

Step 5: Check _ts System Property

```sql -- Cosmos DB uses _ts for TTL calculation -- _ts is last modified timestamp (Unix epoch seconds)

SELECT c.id, c._ts, c.ttl, DateTimeFromParts(1970,1,1) + Duration(c._ts * 1000) as LastModified FROM c

-- TTL expiration = _ts + ttl -- If _ts is missing or wrong, TTL won't work correctly ```

Step 6: Verify Custom Timestamp Property

```bash # If using custom timestamp for TTL, check it's properly formatted # Must be ISO 8601 format or Unix timestamp

# Using Path annotation in Java: @Container(containerName = "items") public class Item { @Id private String id;

@TimeToLive // Maps to ttl property private Integer timeToLive;

private String data; } ```

Step 7: Check for TTL Override

```sql -- Documents with ttl = -1 never expire SELECT * FROM c WHERE c.ttl = -1

-- These override container default TTL -- Update them to enable expiration: UPDATE c SET c.ttl = 3600 WHERE c.ttl = -1 ```

Step 8: Monitor TTL Deletion

```bash # TTL is a background process, deletions may not be immediate # Check container metrics for delete operations

az monitor metrics list \ --resource /subscriptions/SUB/resourceGroups/my-rg/providers/Microsoft.DocumentDB/databaseAccounts/my-cosmos \ --metric "DocumentCount" \ --interval PT1H

# Document count should decrease as TTL expires documents ```

Step 9: Force Immediate Deletion (Development)

```csharp // For testing, manually delete expired documents var query = container.GetItemQueryIterator<dynamic>( "SELECT * FROM c WHERE c._ts + c.ttl < GetCurrentDateTime()");

while (query.HasMoreResults) { var page = await query.ReadNextAsync(); foreach (var doc in page) { await container.DeleteItemAsync<dynamic>( doc.id.ToString(), new PartitionKey(doc.id.ToString())); } } ```

Step 10: Set Up TTL Monitoring

```bash # Alert when document count isn't decreasing as expected az monitor metrics alert create \ --name cosmos-ttl-alert \ --resource-group my-rg \ --scopes /subscriptions/SUB/resourceGroups/my-rg/providers/Microsoft.DocumentDB/databaseAccounts/my-cosmos \ --condition "avg DocumentCount > 100000" \ --window-size 1h

Cosmos DB TTL Behavior

TTL ValueBehavior
-1Never expire (container or document level)
nullUse container default TTL
0Expire immediately
> 0Expire after N seconds

Verification

```bash # After enabling TTL, wait for background process # TTL cleanup can take up to a few minutes

# Check documents are being deleted az cosmosdb sql container show \ --account-name my-cosmos \ --resource-group my-rg \ --database-name my-db \ --name my-container \ --query 'resource.documentCount'

# Should decrease over time as documents expire

# Query for old documents az cosmosdb sql query \ --account-name my-cosmos \ --database-name my-db \ --container-name my-container \ --query-text "SELECT * FROM c WHERE c._ts < 1700000000"

# Should return fewer results over time ```

  • [Fix Azure Cosmos DB Throttling](/articles/fix-azure-cosmos-db-throttling)
  • [Fix Azure Cosmos DB Partition Hotspot](/articles/fix-azure-cosmos-db-partition-hotspot)
  • [Fix Azure Cosmos DB SQL Query Slow](/articles/fix-azure-cosmos-db-sql-query-slow)
  • [Technical troubleshooting: Fix Azure Aks Pod Crashloopbackoff Issue in Azure](azure-aks-pod-crashloopbackoff)
  • [Technical troubleshooting: Fix Azure Api Management Policy Expression Runtime](azure-api-management-policy-expression-runtime-error)
  • [Technical troubleshooting: Fix Azure App Configuration Feature Flag Not Refre](azure-app-configuration-feature-flag-not-refreshing)
  • [Technical troubleshooting: Fix Azure App Service 503 Always On Disabled Issue](azure-app-service-503-always-on-disabled)
  • [Technical troubleshooting: Fix Azure Application Gateway Err SSL Unrecognized](azure-application-gateway-err-ssl-unrecognized-name-alert)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Fix Azure Cosmos DB TTL Not Expiring Documents", "description": "Troubleshoot Cosmos DB TTL not expiring documents. Check TTL configuration, timestamp properties, and container settings.", "url": "https://www.fixwikihub.com/fix-azure-cosmos-db-ttl-not-expiring", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-04-02T18:14:51.667Z", "dateModified": "2026-04-02T18:14:51.667Z" } </script>