# Cost management

> Expected monthly cost of the customer template (about USD 725–755 at list price, before model usage), pricing the models, the four spend limits and an AWS budget.

_How-to · Updated 2026-10-17 · Describes release 0.1.164_

Evidence reviewed **8 October 2026** on **0.1.130** (the 16 October 2026 handover build; see the [release notes](/help/release-notes.md)); the template defaults priced here are unchanged through 0.1.133.

Price the models before relying on spend ceilings. The customer values pack sets **USD 10 per investigation** for a workspace with no saved override. Cloud infrastructure budgets are separate and belong at the customer's billing/payer account.

## Expected monthly cost {#expected-monthly-cost}

No month of a customer-template installation has been measured: every cost line in [Deployment runbook §6](/help/ops-installation-runbook.md#6-costs-and-timings) reads "not measured". The figures below are AWS list prices for `ap-southeast-1` at 730 hours a month, from the cost model (AWS Price List API, 13 August 2026; the RDS class re-priced 6 October 2026), applied to the template defaults in `infrastructure/iac/envs/_customer-template/env.hcl`. Other regions are priced differently.

| Line | Template default | USD a month |
|---|---|---:|
| EKS control plane | One cluster | 73.00 |
| Nodes (EKS Auto Mode) | Sized from the pods' requests; the cost model sizes the platform alone (about 5.2 vCPU and 12 GiB of requests) at 2× `m6a.xlarge`, each with an 80 GB gp3 volume | 368.56 (instances 315.36, Auto Mode fee 37.84, volumes 15.36) |
| NAT gateway | One, shared by both zones | 43.07, plus 0.059 per GB processed |
| Application Load Balancer | One | about 18.40 |
| RDS PostgreSQL | `db.t4g.medium`, Multi-AZ | about 149 (74.46 single-AZ; Multi-AZ is about twice the instance cost) |
| RDS storage | 20 GB gp3, autoscaling up to 100 GiB | 2.76 at the single-AZ gp3 rate, growing with the data |
| Interface endpoints | ECR api, ECR dkr, Secrets Manager, STS and Bedrock runtime, each in two zones | about 70–100, plus a per-GB charge |
| **Total** | | **about 725–755** |

Not in the total: model usage (below), data transfer, public IPv4 addresses (0.005 per address-hour), Secrets Manager (0.40 per secret a month), CloudWatch logs and alarms, RDS backup storage, the EKS secrets-encryption KMS key, support plans and tax.

What moves the figure (`env.hcl` defaults in parentheses):

| Knob | Effect |
|---|---|
| `rds_multi_az` (`true`) | `false` drops the standby, about −74 a month; an availability-zone outage then stops the database until the zone recovers |
| `enable_core_endpoints` (`true`) | `false` drops the ECR, Secrets Manager and STS endpoints, about −60 to −80 a month; that traffic then pays NAT processing |
| `enable_bedrock_endpoint` (`true`) | `false`, for an OpenAI-compatible endpoint or a region without Bedrock, drops one endpoint pair, about −14 to −20 a month |
| `single_nat_gateway` (`true`) | `false` adds a NAT gateway per zone, about +43 a month |
| `rds_instance_class` (`db.t4g.medium`) | The RDS lines follow the class |
| Workspaces | Each workspace runs its own runner pod, so Auto Mode adds node capacity as workspaces are added |

**Who sets which knob.** `env.hcl` (the knobs above and the AWS cost guardrails under [Reconcile and export](/help/ops-expected-cost.md#reconcile-and-export)) and the deployment variables in the values file (`AIOPS_MAX_COST_USD_DEFAULT`, `AIOPS_PRICE_SYNC_INTERVAL_S`, `AIOPS_MODELS_DEV_PRICES`) live on the vendor's customer branch: ask the vendor to change them, and the next deploy applies them. The model price map is yours, under Ops → Platform defaults → **Model price map**. Each workspace's four limits and its pause belong to that workspace's owners.

**Model usage** comes on top: on Bedrock it is billed in your AWS account, and the platform books it per run ([Reconcile and export](/help/ops-expected-cost.md#reconcile-and-export)). The customer values cap each investigation at USD 10 for a workspace with no cap of its own; the daily, monthly and per-incident limits stay off until a workspace owner sets them. The one recorded per-incident figure comes from the vendor's test lab, not from a customer estate: the certified incident-agent run booked a mean of USD 0.87 of model spend per graded investigation, on the lab's models and estate (iteration playbook); the validation runs after a fix are booked on top. Read your own spend on the portal's **Usage & billing** page once real incidents arrive, and set the monthly limit from it.

## Make booked model cost meaningful {#make-booked-model-cost-meaningful}

Platform administrator: Ops → Platform defaults → **Model price map**.

1. Press **Fetch list prices**. For every model in use that the gateway serves from AWS Bedrock, the platform reads the public AWS Price List for the region the gateway calls from (global rows for a `global.` profile, regional rows for `apac.`/`us.`/in-region ids) and records input, output, cache-read and 5-minute cache-write rates with the price list's publication as the source. The control plane repeats this daily (`AIOPS_PRICE_SYNC_INTERVAL_S`, default 86400, `0` = off for an install without outbound HTTPS to `pricing.us-east-1.amazonaws.com` and `models.dev`; a deployment variable the vendor sets in your values file, so ask for it with the install). A gateway entry must state `aws_region_name` to be priced from AWS. Every model **not** served through Bedrock is priced at its publisher's list price as models.dev (an MIT-licensed community dataset, `https://models.dev/api.json`) publishes it — the publisher's own entry only — through a dated mapping: the reviewed router table maps `cx/` (OpenAI), `glm/` (Z.ai), `ds/` (DeepSeek) and `cc/` (Anthropic) aliases; every other model is mapped by the operator (**Map**, suggestions first). A change beyond 2x waits for **Confirm**. `AIOPS_MODELS_DEV_PRICES=false` turns this part off.
2. For a contract or discounted rate, record the price in the editor with a **source note** (the contract or pricing page). A manual price outranks the list price; the daily sync never replaces it unless the operator names the model (**Replace with the list price**).
3. Read **Needs attention**: every model in use without a trusted price, its 30-day spend, the estimated part and the reason (no region on the gateway entry, no reviewed AWS mapping, not mapped to a publisher model, a held change, a gap models.dev cannot state). Models priced only from `AIOPS_MODEL_PRICES` show as *source not recorded* until mapped or until someone records a note for them. A workspace on its own endpoint is priced the same way (**Workspace endpoints**): what its endpoint reports is never the price; an unmapped model books USD 0 as *Not priced yet*.

A model without a trusted price still books — from the runtime library's own price table (pinned to the release, labelled an estimate) or the `AIOPS_PRICE_PER_1M_*` fallback — so dollar limits keep working; every cost surface shows that part as an **estimate**. An unknown gateway alias with no price anywhere books **USD 0** despite token consumption; its dollar-based limits cannot work. Missing cached-read pricing charges cached input at the full input rate; the breakdown flags the over-estimate. Cache writes bill at `cache_write_per_1m` (Anthropic on Bedrock: about 1.25× input).

Prices keep their history: a new price applies from its date and each run is priced at the price in force when it started. Re-pricing history is an explicit platform action (`POST /api/aiops/admin/rebook-costs`, dry run first); to correct the past, record the price with **Apply from** set to when it was really in force, then re-book. A price applies from its date on, over every price recorded before it (a contract rate back-dated over the AWS list price included). A save over a card that changed since the page loaded (the daily sync, another administrator) is refused: reload and edit again. Confirm rates before publishing a spend report.

## Four ceilings and the pause control {#four-ceilings-and-the-pause-control}

| Control | Where and when enforced | Important boundary |
|---|---|---|
| Per-investigation `max_cost_usd` | Guardrails & cost → Cost cap; checked before the next model call | Customer default `AIOPS_MAX_COST_USD_DEFAULT=10`; a saved workspace cap wins; saved 0 means uncapped, not inherited; an empty field follows the default |
| Daily `daily_cost_limit_usd` | Guardrails & cost; new run starts, spend since UTC midnight | 0 means no daily limit; platform daily default applies only when no workspace value exists |
| Monthly `monthly_cost_limit_usd` | Guardrails & cost; new run starts, spend since month start UTC | 0/absent means no monthly limit |
| Per-incident `incident_cost_limit_usd` | Aggregate of triage, re-analysis and verify runs | Exhausted incident escalates; authorized human can grant extra budget on the incident or take it manually |
| **Pause all agents** (`agents_paused`) | Workspace-wide stop/start guard | Alerts/incidents still record; blocked starts fail and escalate; in-flight stops are observed at checkpoints |

The four limits and the pause are the workspace's `PATCH /api/aiops/{tenant_id}/guardrails` keys, named as in the table: `max_cost_usd`, `daily_cost_limit_usd`, `monthly_cost_limit_usd`, `incident_cost_limit_usd` and `agents_paused`. The call needs `members.manage` (a write-scope personal access token of a workspace owner) and changes only the keys it sends; `0` turns a limit off, and `"max_cost_usd": null` returns the workspace to the platform default. With example amounts:

```bash
curl -X PATCH https://<your-host>/api/aiops/<tenant-id>/guardrails \
  -H "Authorization: Bearer <workspace owner's PAT, ntp_…>" \
  -H "Content-Type: application/json" \
  -d '{"daily_cost_limit_usd": 50, "monthly_cost_limit_usd": 1000, "incident_cost_limit_usd": 20}'
```

Set and review the workspace's daily, monthly and incident limits explicitly; the USD 10 per-run default does not set those aggregate limits. Day/month ceilings gate **starts**, so concurrent runs can overshoot an aggregate ceiling. The step cap checks before the next call, so the final call can cross the numerical limit; it is not a prepaid-provider hard stop.

At 70% of a priced per-run cap the engine can step down its model ladder once. At the cap it stops without a further wrap-up call and leaves a stop notice for human handling. A pinned pillar model disables ladder routing. Best-of-2, when enabled, doubles that run's configured cap. X-ray uses the scan budget supplied at launch instead of the ordinary investigation cap. Keep each of those exceptions visible in capacity/spend planning.

## Respond to a spend alarm {#respond-to-a-spend-alarm}

1. Open Guardrails & cost and inspect the exact stop reason and booked spend.
2. For unexpected spend, enable **Pause all agents** and account for active runs. Use **Stop this run** to request cancellation of a specific run and verify its terminal state.
3. Check model ids/rates, routing, incident recurrence and repeated re-analysis. Restore service only after the owner decides a justified budget or manual handling path.
4. Daily starts resume after UTC midnight, monthly starts after the next month starts, or when a permitted owner raises the limit. A paused workspace requires an explicit resume.

The external `AiopsBudgetTrip` rule sees first trips and should page an independent receiver. It does not discard incoming signals.

A scanner pull can start paid model work in three ways: a finding with a supported prepared fix starts its prepared-fix analysis (`aiops_compliance_auto_triage`, on by default); a newly pulled P1 or P2 finding starts an analysis when **Analyse P1/P2 findings automatically** is on (the shipped default, overridable per workspace on **Guardrails & cost**); and backlog catch-up, off by default, analyses already-held P1/P2 findings. Read the workspace's effective switches and the day's actual spend before a manual pull, and review the Scanner sync interval together with the workspace limits. Keep a price-source failure apart from a model-call failure: *Not priced yet* is a missing price, not a zero-dollar cost, and an estimated price is not a provider invoice.

## Reconcile and export {#reconcile-and-export}

Ops **Billing** rolls up model spend by workspace and class for a UTC calendar month. Use **Download CSV** for that month; it has one row per workspace/class plus a grand total, its `estimated_usd` column is the part of each cost priced from an estimate, its `unpriced_tokens` column the tokens no price was recorded for (they show `$0`: resolve them before invoicing), and its `free_checks` column the validations that ran no agent (`$0`; `runs` counts agent runs only). Operating, verify, discovery and test classes are distinguishable, and lab spend has its own subtotal. The API counterpart is `GET /api/aiops/admin/billing.csv` with an admin-scope platform token. The portal's Spend views and incident breakdowns explain runs, cached reads, cache writes and the source of each price; the estimated part of a month is marked on Ops Billing and on the workspace statement. Benchmark spend is excluded from operating rollups; report test cost separately. Reconcile booked token pricing with provider invoices and note changes to rates or re-booked history in the monthly report.

**Token reconciliation with AWS.** Run, in the control-plane pod, `kubectl -n aiops exec deploy/aiops -- python -m app.aiops.price_sources reconcile --days 7`: it compares the tokens the platform booked per Bedrock model id with CloudWatch `AWS/Bedrock` `InputTokenCount`, `CacheReadInputTokenCount`, `CacheWriteInputTokenCount` and `OutputTokenCount` and reports the drift per model — *match* within 2%, *aws-higher* when other callers share the account, *ours-higher* to investigate. Read-only; it needs `cloudwatch:GetMetricData` on the control plane's role and says so when that is missing. In a customer's own AWS account (BYOC), reconcile the money in Cost Explorer: filter Service to the price list's service name (for example *Claude Sonnet 5 (Amazon Bedrock Edition)*, billed through AWS Marketplace), group by Usage type, and compare with the usage types each AWS price cites (Model price map → Prices in force) and the workspace's Usage & billing → By model for the same UTC month.

For AWS infrastructure, the cloud-finance team manages Budgets and anomaly detection in the **payer account** when the installation account is an Organization member. Enabling template cost guardrails in a member that does not own billing can fail with AccessDenied. In an eligible standalone account, the optional budget emails actual 80%/100% and forecast 100%; the daily anomaly summary threshold is USD 100. Confirm the alarm mailbox and existing anomaly monitor before enabling these settings. They live in the installation's `env.hcl` (`enable_cost_guardrails`, `cost_budget_monthly_usd`, `alarm_email`, `cost_anomaly_monitor_arn`) on the vendor's customer branch: ask the vendor to set them, and the next deploy creates the budget `aiops-<slug>-monthly`.

To set the budget yourself in an account that owns its billing, the same thresholds from AWS CloudShell (example amount and address; a different budget name from the template's, so a later template budget does not clash):

```bash
aws budgets create-budget --account-id 123456789012 \
  --budget '{"BudgetName":"platform-account-monthly","BudgetType":"COST","TimeUnit":"MONTHLY","BudgetLimit":{"Amount":"1000","Unit":"USD"}}' \
  --notifications-with-subscribers '[
    {"Notification":{"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN","Threshold":80,"ThresholdType":"PERCENTAGE"},
     "Subscribers":[{"SubscriptionType":"EMAIL","Address":"cloud-finance@example.com"}]},
    {"Notification":{"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN","Threshold":100,"ThresholdType":"PERCENTAGE"},
     "Subscribers":[{"SubscriptionType":"EMAIL","Address":"cloud-finance@example.com"}]},
    {"Notification":{"NotificationType":"FORECASTED","ComparisonOperator":"GREATER_THAN","Threshold":100,"ThresholdType":"PERCENTAGE"},
     "Subscribers":[{"SubscriptionType":"EMAIL","Address":"cloud-finance@example.com"}]}]'
```

You should see the budget under Billing and Cost Management → Budgets; each address receives AWS's alerts when a threshold is crossed. The account is dedicated to the platform, so the budget needs no cost filter.

Sources: Customer values, customer template, cost model, official price sources, [Agents and guardrails](/help/agents-and-guardrails.md), budget implementation, [Platform Billing](/help/ops-platform.md#billing), and [Deployment runbook §6](/help/ops-installation-runbook.md#6-costs-and-timings).


## Next steps

- Next: [Set up your first customer](/help/ops-start.md)
- [All Install and operate the platform pages](/help/install-and-operate-the-platform/llms.txt)
- [Documentation home](/help/)
