Spend Controls
Two independent caps gate new deployments at the provisioning scheduler. Throttle compares the live $/hr burn rate (catalog $/hr summed across every running VM, plus the candidate's $/hr) against its cap. Budget compares calendar-window spend (settled cost + catalog estimates for VMs whose billing hasn't landed) against its cap. Neither kills running jobs.
| Primitive | Horizon | Catches | Clears |
|---|---|---|---|
| Throttle | Instantaneous | Concurrent fleet $/hr | When VMs end |
| Budget | Calendar | Sustained drift | Day / week / month |
Use them together: throttle caps how much you can be burning at once; budget caps total exposure across a calendar period.
How it works
When a deployment is queued, the scheduler checks every active cap before dispatching. If any cap is at or over its limit, the deployment stays Queued with a "blocked by …" reason visible in anycloud status and anycloud list. Once the cap clears — running VMs finish (throttle), the calendar rolls over (budget), or you raise the cap — the next scheduler tick dispatches it automatically.
Running jobs are never killed by a cap. Throttle and budget only gate the queued → provisioning transition.
When a cap blocks
anycloud submit always accepts — the deployment id comes back immediately. The cap is enforced later, at the dispatch step. A blocked deployment stays Queued with a blocked by … message in anycloud status and anycloud list:
- Throttle:
blocked by throttle: rate $X/hr ≥ cap $Y/hr - Budget:
blocked by daily|weekly|monthly budget: spent $X / $Y — resumes YYYY-MM-DD HH:MM UTC
Budget messages name the next UTC reset; throttle clears whenever running VMs end, so there's no fixed time to show.
Nothing else fires — no email, Slack, or webhook. The deployment waits indefinitely; the scheduler re-checks every few seconds and dispatches as soon as the cap clears. Cancel with anycloud cancel <id> if you don't want to wait.
Throttle
Burn-rate cap ($/hr). Blocks new dispatches when the live fleet's $/hr (running VMs + candidate) is at or over the cap. The moment running VMs end, the burn rate drops — no trailing-window ghost.
anycloud throttle set 20 # account-wide: $20/hr
anycloud throttle set 5 --agent-session # each agent session: $5/hr
anycloud throttle show
anycloud throttle unset
anycloud throttle unset --agent-session
Two caps coexist: account-wide (default) and per agent-session.
Budget
Calendar-window cap. Blocks new dispatches when window-to-date spend is at or over the cap.
anycloud budget set 100 --per day
anycloud budget set 500 --per week
anycloud budget set 2000 --per month
anycloud budget set 50 --per day --agent-session
anycloud budget show
anycloud budget unset --per day
Windows reset at UTC boundaries:
- day — 00:00 UTC
- week — Monday 00:00 UTC
- month — 1st of month, 00:00 UTC
Up to six budgets coexist (three windows × two scopes). When multiple budgets are active, the lowest binds — anycloud budget show names which one is closest to its cap.
Scopes
- account (default) — counts every deployment, including interactive
anycloud submitfrom a human shell. - agent-session — counts only deployments from one specific agent run (Claude Code, Cursor, Codex, Aider). Each session has its own counter. Interactive submits have no session, so they're not bound by agent-session caps — pair with an account-wide cap if you want to bound humans too.
Auto-detection of agent sessions lives in the CLI (see the job-lifecycle attribution column). A session that doesn't auto-detect won't count against agent-session caps.
What each cap measures
-
Throttle sums the catalog
$/hrof every VM currently running. No window, no proration. The candidate's$/hris added to that sum before the comparison, so a burst within one scheduler tick can't all dispatch on the same pre-burst snapshot. -
Budget sums per-VM cost over the calendar window from two sources:
- actual — settled cost from the cloud's billing API, prorated by the window intersection. Lands ~24h after a VM terminates.
- estimated — catalog
$/hr × elapsed_within_windowfor VMs whose actual cost hasn't settled.
anycloud budget showprints both so you can see how much of the total is still pending billing-API reconciliation.
Design notes
- Throttle pre-charges the candidate. Each candidate's
$/hris added to the live burn rate before the comparison, so a burst submitted on the same scheduler tick can't all dispatch on the same pre-burst snapshot. Falls back to reactive (no pre-charge) when the catalog can't price the candidate. - Budget is reactive. Calendar windows are large enough that a tick's worth of overshoot is negligible.
- Single chokepoint. Both caps gate at the provisioning scheduler — the only path from "queued" to a real VM.
anycloud submitalways accepts; if a cap is at its limit, the deployment stays Queued instead. - Polling, not events. Each tick (a few seconds) re-evaluates every queued row. No event subscriptions or callbacks needed.
What's not included (yet)
- Threshold alerts (50/75/90/100%) — hard caps only.
- Forecasting.
- Webhooks on threshold.
- Pause / kill of running jobs.
- Scopes beyond
agent-session(no per-cred, per-region, per-agent-name). - Per-job duration estimates /
--max-durationrequirement.