Cosmos DB accounts on manual throughput using under 10% of their provisioned RU/s
What does ZopNight detect here?
Azure Cosmos DB bills manual provisioned throughput every hour for the RU/s you set, however many are consumed. ZopNight flags manual-throughput accounts whose `NormalizedRUConsumption` averages under 10% over 30 days, sizes a new RU/s from the observed peak plus 20% (never below 400), and prices the proportional reduction.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-247 |
| Category | rightsizing |
| Severity | medium |
| Metric | NormalizedRUConsumption |
| Threshold | average below 10% |
| Evaluation window | 30d |
| Source | ZopNight |
| Permissions used | Microsoft.DocumentDB/databaseAccounts/read · Microsoft.Insights/Metrics/Read |
Where it applies
Manual RU/s is paid whether used or not
With standard (manual) provisioned throughput, Microsoft’s offer comparison says billing is per hour for the RU/s provisioned, regardless of how many RUs were consumed. Teams often set a container to a comfortable round number at launch and never look again. If the busiest partition rarely climbs past a few percent, most of those RU/s are paid for and unused.
Reading RU utilisation
NormalizedRUConsumption is a 0 to 100% metric that
Microsoft defines
as the maximum RU/s utilisation across all partition key ranges, emitted each minute:
az monitor metrics list \ --resource /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.DocumentDB/databaseAccounts/<account> \ --metric NormalizedRUConsumption TotalRequests --aggregation Average Maximum --interval PT1H --offset 30d
az cosmosdb sql container throughput show --resource-group <rg> --account-name <account> \ --database-name <db> --name <container>Why only manual throughput qualifies
Autoscale bills differently: it scales between 10% of the maximum and the maximum, and charges each hour for the highest RU/s it reached, per the autoscale guide. An idle autoscale account already sits near its floor, so reducing its maximum does not save money in proportion. ZopNight therefore fires only when the account’s throughput mode is manual. An account where any container uses autoscale is treated as autoscale, and an unknown mode means no finding.
Gates and the new target
- The account’s current provisioned RU/s and its throughput mode are known, and the mode is manual.
- Average
NormalizedRUConsumptionover the last 30 days is below 10%. - At least 7 days of true peak readings exist, so a smoothed average cannot hide a spike.
- The peak in RU/s is estimated as current RU/s x peak normalized percentage. Because the metric follows the busiest partition, that estimate leans high, which errs toward keeping capacity.
- The target is the peak plus 20% burst room, rounded to the 100 RU/s grid and never below 400 RU/s, the manual minimum Microsoft documents. If no safe smaller value exists, there is no finding.
The proportional saving
saving = monthly cost x (current RU/s - target RU/s) / current RU/sZopNight treats manual throughput as billed linearly per 100 RU/s, so the saving scales with the reduction. Accounts replicated to several regions for non-production use have a separate check: Azure Cosmos DB Dev/Test with Multi-Region Replication.
Lowering provisioned throughput
- Review Normalized RU Consumption and the rate of throttled (429) requests per container.
- Lower the RU/s on the container or shared database:
az cosmosdb sql container throughput update --resource-group <rg> --account-name <account> --database-name <db> --name <container> --throughput <target>. - Watch for 429 responses for a few days and raise the value if they climb.
- For spiky workloads, consider migrating to autoscale with
az cosmosdb sql container throughput migrate --throughput-type autoscale.