DEV Community

Multigrid
Multigrid

Posted on • Originally published at multigrid.ai

Cost Management Alerts for Container Apps Running Inference

Azure budgets attach to a billing scope, and a Container Apps environment is not one. That single fact determines the whole design of this alert, and it is the reason the obvious approach produces a budget that watches the wrong things.

Pick the scope before the threshold

Microsoft documents the Microsoft.Consumption/budgets resource type as deployable at tenant, management group, subscription and resource group scopes. A Container Apps environment sits inside a resource group; it is not itself a scope you can budget against.

Two workable shapes follow, and the choice is architectural rather than cosmetic. Either give the Container Apps environment its own resource group, so the resource-group budget is exactly the environment’s spend, or keep it in a shared group and use a budget filter on a tag such as workload=inference. The first is cleaner and constrains how you lay out resources; the second is more flexible and depends on the tag being applied without exception.

Scope also decides whether you can automate a response. Microsoft documents action group support on budgets for subscription and resource group scopes only. A management-group budget can notify by email but cannot call an action group, so if the plan involves doing something automatically when the threshold trips, the budget has to be at subscription or resource group level.

Creating the budget

Budgets are created through the Consumption API, which means the CLI, an ARM or Bicep template, or Terraform — all of which are better than the portal here, because a cost control that exists only as console state is one nobody can review.

az consumption budget create-with-rg \
  --resource-group rg-inference-prod \
  --budget-name inference-monthly \
  --amount 4000 \
  --time-grain Monthly \
  --start-date 2026-09-01 \
  --end-date 2027-09-01 \
  --category Cost
Enter fullscreen mode Exit fullscreen mode

As Bicep, which is the form worth committing:

resource budget 'Microsoft.Consumption/budgets@2021-10-01' = {
  name: 'inference-monthly'
  properties: {
    category: 'Cost'
    amount: 4000
    timeGrain: 'Monthly'
    timePeriod: {
      startDate: '2026-09-01'
    }
    notifications: {
      Actual_GreaterThan_80_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 80
        thresholdType: 'Actual'
        contactGroups: [ actionGroup.id ]
      }
      Forecasted_GreaterThan_100_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 100
        thresholdType: 'Forecasted'
        contactGroups: [ actionGroup.id ]
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The pairing of an Actual notification with a Forecasted one is the part worth copying. An actual-only budget tells you on the 28th that you overspent; a forecast notification tells you on the 9th that the current run rate lands over budget, which is the only one of the two you can act on. Azure derives the forecast from the period’s spend so far, so it is noisy early in the month and useful from the second week.

Wiring an action group

A budget alert only notifies. If the goal is to change something, the action group is where that happens — it can send email and SMS, but it can also call a webhook, an Azure Function, a Logic App or an automation runbook.

  1. Create the action group in the same subscription as the budget.
  2. Add a notification receiver for the people who need to know, and a webhook or function receiver for whatever should happen.
  3. Reference the action group’s resource id in contactGroups on each notification, as above.
  4. Give the function or runbook a managed identity with only the permissions its action needs — typically Contributor scoped to the one Container App, not the subscription.
  5. Make the automated action reversible and log it. The most common useful action is scaling the app’s minimum replica count to zero, which stops idle charges without deleting anything.

What the alert can and cannot see

Container Apps on the Consumption plan bills on resource allocation measured in vCPU-seconds and GiB-seconds, plus a charge for HTTP requests. Microsoft’s pricing page documents a monthly free grant per subscription of 180,000 vCPU-seconds, 360,000 GiB-seconds and 2 million requests, and distinguishes an active rate from a lower idle rate that applies when a replica is running at the minimum replica count without processing requests. Microsoft publishes the meters and rates here.

The free grant amounts and the active and idle rates are current-vendor pricing read in August 2026, and both the figures and the active/idle threshold definitions have changed before. Confirm on the pricing page before building a forecast on them.

Two consequences for the alert. The free grant means a small environment can show close to zero cost for weeks and then produce a bill that looks like a step change when the grant is exhausted — which reads like an incident and is not one. And because the app can scale to zero replicas with no usage charge at all, a budget that never fires may simply mean the app is idle, not that spend is under control.

Most importantly: the budget sees the Container Apps compute. It does not see the model. If the workload calls Azure OpenAI, that resource lives outside the Container Apps environment and its token charges are a different meter on a different resource — and if it calls a first-party provider API, the spend is not on the Azure invoice at all. A budget scoped to the compute and labelled “inference spend” is measuring the smaller half.

Latency, and what to do about it

Azure evaluates budgets against Cost Management data, which is compiled periodically rather than streamed. Microsoft documents the alerting behaviour on its cost alerts page; treat the alert as arriving hours after the spend, not minutes. Microsoft’s cost alerts documentation is the place to check the current cadence rather than assuming one.

Because of that lag, the fast signal has to come from somewhere else. Two candidates that are near-real-time: the platform metrics on the Container App itself — replica count and CPU usage, which are the direct inputs to the vCPU-second meter — and, for Azure OpenAI, a metric alert on token counts. Neither is denominated in currency, and both fire in minutes rather than hours.

The workable arrangement is a metric alert for the incident and a budget for the month: one tells you something changed while you can still do something about it, the other guarantees somebody notices even if the first was ignored.

Related

Top comments (0)