Skip to main content
emnode
Cost

Choose Cosmos DB serverless for bursty low-traffic workloads

One capacity decision per Azure Cosmos DB account: when traffic is intermittent and the average sits far below the peak, consumption billing stops you paying around the clock for throughput you only touch for minutes a day.

14 min·10 sections·AZURE

Last reviewed

Cosmos DB capacity modes: the basics

What are you actually choosing between when you pick a Cosmos DB account's capacity mode?

Azure Cosmos DB bills throughput in request units per second, or RU/s, and every account is in one of two capacity modes that decide how those RU/s turn into money. In provisioned throughput mode you reserve a fixed number of RU/s, with a manual floor of 400 RU/s per container, and you pay for that reservation every hour whether or not a single request arrives. In serverless mode you reserve nothing: you are billed only for the RUs your operations actually consume and for the storage your data takes up, with no minimum charge and no capacity to plan. The same queries run identically in both modes; only the billing shape differs.

The mismatch that costs money is a workload whose average traffic sits far below its peak. A provisioned container holds a fixed reservation open 24 hours a day, so an internal tool that is busy for an hour each morning and idle the rest of the time still pays the full reservation overnight, at weekends and on holidays. Microsoft positions serverless squarely at this shape: workloads with bursting, intermittent traffic that is hard to forecast and a low, under roughly ten percent, average-to-peak ratio. For those, consumption billing can be dramatically cheaper than holding a provisioned floor open the whole time.

This is a real fork, not a slider you can nudge later. Capacity mode is chosen when the account is created, and you cannot flip an existing provisioned account to serverless in place: you create a new serverless account and migrate the data across. So the cost win comes from recognising the bursty, low-traffic accounts early, standing the right ones up as serverless, and not defaulting every new database to provisioned out of habit.

In this lesson you will learn how Azure Cosmos DB bills throughput in its two capacity modes, how to spot the bursty, low-traffic accounts where a provisioned floor is quietly wasting spend, and how to stand them up as serverless safely, including the one-way constraint that you cannot convert an existing provisioned account in place. It complements Azure Advisor, which flags idle and over-provisioned Cosmos DB throughput but recommends autoscale rather than serverless, so this lesson covers the case Advisor does not.

Fun fact

The ten percent rule of thumb

Microsoft's own guidance puts a number on when serverless wins: it is aimed at workloads with a low, under roughly ten percent, average-to-peak traffic ratio. That is a precise way of describing the database that spikes for a few minutes and then sits idle: if your sustained usage is a small fraction of your busiest moment, you are reserving for the peak but paying for it during all the quiet hours in between. Serverless inverts that by charging only for the requests that actually land, with no minimum, so the long idle stretches that dominate a bursty workload cost nothing at all rather than the full reserved floor.

Finding Cosmos DB accounts that are paying for idle throughput

Priya runs the platform team at a SaaS company with a sprawl of small Cosmos DB accounts: dev databases, a couple of internal tools, an early-stage feature store. Azure Advisor has flagged several of them as having no activity for weeks, and the finance partner has asked why a set of barely used databases shows up as a steady monthly charge.

Rather than guess, Priya lists the accounts and reads each one's capacity mode straight from its capabilities, so the provisioned accounts paying a floor around the clock can be told apart from the serverless ones that already bill on consumption.

List every Cosmos DB account and show whether it is serverless. An account is serverless when its capabilities include EnableServerless; anything else is provisioned and paying a floor.

$ az cosmosdb list --query "[].{name:name, rg:resourceGroup, mode:capabilities[?name=='EnableServerless'].name | [0]}" -o table
Name Rg Mode
---------------- -------------- ----------------
internal-tools rg-shared None
feature-store-dev rg-dev None
orders-prod rg-prod None
events-ingest rg-shared EnableServerless
# internal-tools and feature-store-dev are provisioned but barely used: candidates.

Mode of None means provisioned, so the account pays its reserved floor every hour. Cross-reference the provisioned accounts against Advisor's idle and low-activity flags to find the ones worth moving to serverless. orders-prod is steady production and should stay provisioned.

How serverless billing differs from a provisioned floordeep dive

Provisioned throughput is a reservation. You set a number of RU/s, with a manual floor of 400 RU/s per container, and Azure bills you hourly for the highest throughput provisioned during that hour in 100 RU/s increments, whether the requests arrive or not. Autoscale softens this by scaling the reservation between ten percent and one hundred percent of a maximum you set, but it still bills a floor for every hour and, because the autoscale rate per RU/s is higher than standard, it only pays off when you are below the maximum for a good share of the month. Either way, an idle provisioned container is still costing money.

Serverless removes the reservation entirely. There is no RU/s to set: you are billed per million request units your operations actually consume, plus the transactional storage your data occupies, with no minimum charge. An account that serves a burst in the morning and nothing for the rest of the day is billed for that burst and the stored data, and the idle hours are free. That is precisely why a low average-to-peak ratio is the deciding signal: the more of the day a workload spends idle, the more a consumption model beats a reserved one.

The trade-offs are real and worth naming. Serverless gives no guarantee of predictable throughput or latency, it runs in a single Azure region per account, and a serverless container is capped at 5,000 RU/s per physical partition, so its ceiling scales with partitions rather than being dialled up on demand. And the choice is sticky: capacity mode is fixed at account creation, you cannot convert an existing provisioned account to serverless in place, and the supported serverless-to-provisioned migration is one way only. Those constraints are exactly why serverless fits bursty, low-traffic accounts and not steady, high-throughput ones, and why the decision is worth making deliberately up front.

What is the impact of leaving a bursty workload on provisioned throughput?

The direct impact is paying for idle capacity. A provisioned account bills its reserved floor every hour of every day, so a workload that is genuinely active for a small slice of the day pays many times over for throughput it never touches. On a single small database the waste is easy to ignore, but a fleet of dev databases, internal tools and low-traffic services each holding a reserved floor turns into a standing monthly charge for capacity that is mostly asleep.

The second-order impact is that the waste is invisible and compounding. Because provisioned is the comfortable default, every new low-traffic account gets stood up with a floor, and nobody revisits the choice once the database works. The bill grows quietly with the number of accounts rather than with actual usage, which is exactly the pattern cost reviews are meant to catch and exactly the one that hides best because no single line looks large.

The flip side is that the fix is concentrated and low-risk. The accounts that benefit are easy to characterise, the workload behaviour does not change when you move to serverless, and the recurring cost can only fall for a database that was paying for idle reservation. The one genuine cost to weigh is predictability: serverless makes the bill follow demand, so a workload that unexpectedly turns steady and high-volume would cost more on serverless than provisioned. For a genuinely bursty, low-traffic account that risk is small, but it is the reason the move is targeted rather than blanket.

How do you move a bursty workload to serverless safely?

Treat this as a per-account decision driven by traffic shape, not a fleet-wide flag. The order matters: qualify the candidates before you migrate anything, because the choice is one way and you do not want to convert a workload that is actually steady.

1. Inventory every account by capacity mode and activity

List all Cosmos DB accounts and read each one's capacity mode from its capabilities. Cross-reference the provisioned accounts with Azure Advisor's idle and low-activity recommendations and with Azure Monitor RU/s metrics, so you can see which provisioned floors are actually busy and which are mostly asleep.

2. Qualify candidates by average-to-peak ratio

For each provisioned account, look at sustained usage against the peak. A low ratio, broadly under ten percent, with long idle stretches is a serverless candidate. A steady, high-throughput workload is not: leave it on provisioned, and consider autoscale or reserved capacity to optimise it instead. This step is what stops a blanket migration that would raise the bill on the steady accounts.

3. Stand up a new serverless account and migrate

Because you cannot convert a provisioned account to serverless in place, create a new account with the EnableServerless capability and migrate the data across, for example with the data migration tooling or a controlled copy. Validate the workload against the serverless limits first: a single region per account and a 5,000 RU/s per-partition ceiling. If the workload fits those, the move is low risk.

4. Default new low-traffic databases to serverless

Bake the decision into how new accounts are provisioned. In your infrastructure-as-code, set EnableServerless for databases you expect to be bursty or low-traffic so the saving is captured at creation rather than discovered later in a cost review. Keep provisioned as the deliberate choice for steady, high-throughput production stores.

# Stand up a new serverless Cosmos DB account (SQL API) for a bursty, low-traffic workload.
# You cannot convert an existing provisioned account in place, so create serverless new.
az cosmosdb create \
  --name "$NEW_ACCOUNT" \
  --resource-group "$RESOURCE_GROUP" \
  --locations regionName="uksouth" failoverPriority=0 isZoneRedundant=False \
  --default-consistency-level Eventual \
  --capabilities EnableServerless

# Confirm the account is serverless (capabilities should list EnableServerless).
az cosmosdb show --name "$NEW_ACCOUNT" --resource-group "$RESOURCE_GROUP" \
  --query "capabilities[].name" -o tsv

# --- Bicep: default new low-traffic databases to serverless at creation ---
# resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' = {
#   name: newAccount
#   location: location
#   kind: 'GlobalDocumentDB'
#   properties: {
#     databaseAccountOfferType: 'Standard'
#     consistencyPolicy: { defaultConsistencyLevel: 'Eventual' }
#     locations: [ { locationName: location, failoverPriority: 0 } ]
#     capabilities: [ { name: 'EnableServerless' } ]
#   }
# }

Quick quiz

Question 1 of 5

Which workload shape is Azure Cosmos DB serverless designed to be most cost-effective for?

You can now treat Cosmos DB capacity mode as a deliberate cost decision rather than a default: inventory accounts by mode and activity, qualify the bursty, low-traffic ones by their average-to-peak ratio, stand the right ones up as serverless so the idle hours stop costing anything, and bake serverless into how new low-traffic databases are created. The steady production stores stay provisioned on purpose, because that is where they are cheapest.

Back to the library