Budgets and usage
Every Ciresk dev session is metered in Ciresk CI's cost unit, and your organization can cap what sessions use per person per day and for the whole organization per month. This page covers how a session's cost is computed, how an admin sets a budget, what happens when a budget runs out, and how anyone reads where they stand with ciresk dev usage.
The unit
One unit is one vCPU-second, the same unit Ciresk CI counts its jobs in, so ciresk dev usage and ciresk ci budget add up.
A session costs, per second, the larger of its CPUs and its memory in GiB divided by 8:
units per second = max(cpus, memory GiB / 8)- The weight is what the session reserves: the sum of the
cpusandmemoryof every container that starts with the session (its services and prepare steps), as declared in its template (see templates.md). Containers that start on demand, such as the session's shells, are not counted.memory_maxandmemory_ceilingdo not count. - Finished prepare steps stop costing. While a session runs, its weight is what the worker last reported: the declared weight less every prepare step that has completed. A step that runs again counts again.
- Every session costs something: the rate is at least one thousandth of a unit a second.
- Rounding: the rate is computed in thousandths of a unit; each 15-minute slice a session pays for is rounded up to a whole unit, and the unused part given back when a session ends is rounded down.
| Session | CPUs | Memory | Units per second | Units per hour |
|---|---|---|---|---|
| A typical web app stack | 2.45 | 3 GiB | 2.45 | 8,820 |
| A memory-heavy stack | 0.5 | 16 GiB | 2 | 7,200 |
| A small stack | 1 | 4 GiB | 1 | 3,600 |
Use is recorded for every session whether or not your organization sets a budget.
Set a budget
Budgets are part of your organization's dev policy, so an admin changes them with ciresk dev policy set like any other policy field (see admin.md). Changing the policy needs dev:policy.write and is reserved for people: an agent can read a budget and its use, never raise it. Each change is a new policy version and an audit record naming the changed field.
| Field | Default | What it limits |
|---|---|---|
budget.person_day_units | null (no limit) | What one person's sessions use in a UTC day. Sessions started by that person's agents and API keys count against the person. |
budget.org_month_units | null (no limit) | What all of the organization's sessions use in a UTC calendar month. |
Each value is a whole number of at least 1, or null. A 0 is refused with policy_invalid. For example, 100 vCPU-hours a person per day and 20,000 vCPU-hours for the organization per month:
"budget": { "person_day_units": 360000, "org_month_units": 72000000 }ciresk dev policy show prints the budget in one line, for example budget: 360,000 units (100.00 vCPU-hours) per person a day, ..., or budget: none.
Windows
- The person window is the current UTC day and resets at the next UTC midnight.
- The organization window is the current UTC calendar month and resets at 00:00 UTC on the first of the next month.
- A slice counts in the day and month it is paid in: a slice paid at 23:55 UTC counts on that day, even though most of it runs the next.
How a budget is enforced
A session pays ahead, in slices of 15 minutes, and gives back the unused part when it ends. When both windows have a limit, the tighter one (the one with less left) decides.
Starting a session
ciresk dev up pays the first slice when the session is admitted. So does ciresk dev start, which starts a stopped cloud session again from its checkpoint: it is admitted like a new session, against the budget as it is now. Waiting for a worker counts: a session pays from the moment it is admitted, not from the moment it is ready.
Under a budget the first slice is a whole 15 minutes, or what the tighter window has left. When that is less than 5 minutes of the session at its weight, the start is refused with budget_exceeded:
{
"type": "https://docs.ciresk.com/errors/budget_exceeded",
"title": "Budget reached",
"status": 403,
"code": "budget_exceeded",
"detail": "Your dev budget for today cannot pay for 5 more minutes of this session",
"scope": "person_day",
"limit_units": 360000,
"used_units": 358200,
"resets_at": "2026-10-26T00:00:00Z",
"next": "ciresk dev usage"
}| Field | Meaning |
|---|---|
scope | The window that ran out: person_day or org_month |
limit_units | That window's limit |
used_units | What the window has used, including what live sessions have paid ahead |
resets_at | When the window starts again |
next | ciresk dev usage |
ciresk dev exits with code 5 on budget_exceeded. A smaller template, a raised budget or the reset lets the next start through. A refused start is not stored against its idempotency key: the API and the MCP tools replay only a successful answer, so a retry with the same Idempotency-Key (or MCP idempotency_key) starts the session once the budget allows. ciresk dev start sends a new key each time you run it. A stopped session that is refused stays stopped, with its checkpoint.
Scheduled starts
A start from an autostart schedule (ciresk dev schedule set, which starts a stopped cloud session 5 minutes before the time you name) is admitted like ciresk dev start, for the session's owner and against the owner's budget. When the budget refuses it, the session stays stopped, the session's event log records session.autostart_refused with the code, and ciresk dev schedule show names it in the last run: last run ...: started none; refused devs_... (budget_exceeded).
While a session runs
About 5 minutes before a session's paid time ends, it pays the next slice, at the weight its worker last reported, against the current policy.
- A short slice is a notice. When the budget can pay only part of the next slice, the session's owner gets one notice, once per session, in what needs them: the time the session stops and which budget runs out (
stops at 2026-10-25 17:40 UTC: your dev budget for today runs out; ciresk dev usage shows where you stand). It shows inciresk dev status,ciresk dev watchandciresk dev needs, and to agents through the MCP tools, and it clears when the session ends. The session's event log recordssession.budget_warningwithstops_at,scope,limit_units,resets_atandnext. - At the end of the paid time it stops. A session whose paid time has ended and for which nothing more can be paid stops with the reason
budget(session.stop_requestedin its event log). Under the defaultlifecycle.auto_stop: pause, a session that runs from the repository (ciresk dev up --remote) is kept with its files checkpointed, asciresk dev stopkeeps it; a session synced from your worktree ends, since its files are on your machine. Once the budget allows,ciresk dev startbrings a kept session back andciresk dev upstarts a new one. An organization that setslifecycle.auto_stoptoendhas every budget stop end the session. - No grace. A session runs only as long as it has paid for. The time between the end of its paid time and the worker stopping it (seconds) is not charged.
A session always gets at least 5 minutes' notice before a budget stop, and pays at most 20 minutes ahead (the 15-minute slice it pays 5 minutes before its paid time ends). So:
- A lowered budget stops a running session within 20 minutes, never at once.
- A raised budget reaches a warned session at its next payment, before it stops.
What stopped sessions cost
Nothing. When a session ends, or is stopped and kept with its checkpoint, the unpaid part of every slice still ahead of it is given back to the windows the slices were paid in. A kept session pays nothing until it is started again, and starting it pays a first slice like a new session.
See where you stand
ciresk dev usage
ciresk dev usage --jsonIt needs dev:policy.read, which every role holds, and works for people, agents and API keys. It takes no flags of its own besides the global ones (--json, --quiet, --org, --profile, --region, --api-url). It prints two lines:
today (you): 8,820 units (2.45 vCPU-hours) used, 2,205 units (0.61 vCPU-hours) reserved ahead, 348,975 units (96.93 vCPU-hours) left of 360,000 units (100.00 vCPU-hours); resets 2026-10-26T00:00:00Z
this month (organisation): 1,204,500 units (334.58 vCPU-hours) used, 6,615 units (1.83 vCPU-hours) reserved ahead, no budget; resets 2026-11-01T00:00:00Z- today (you) is your UTC day: your own sessions and those your agents and API keys started. An agent sees its owner's day.
- this month (organisation) is the whole organization's UTC month.
- used is what sessions have used; reserved ahead is what live sessions have paid for and not used yet; left is the limit less both. Without a limit the line says
no budget.
With --json, data holds the answer of the API:
{
"unit": "vcpu_seconds",
"person": {
"used_units": 8820,
"reserved_units": 2205,
"limit_units": 360000,
"remaining_units": 348975,
"resets_at": "2026-10-26T00:00:00Z"
},
"org": {
"used_units": 1204500,
"reserved_units": 6615,
"limit_units": null,
"remaining_units": null,
"resets_at": "2026-11-01T00:00:00Z"
}
}limit_units and remaining_units are null when the window has no budget. remaining_units never goes below 0.
For agents
Agents read the same numbers with the MCP tool dev_usage (see mcp.md), and should read it before long work so they can plan instead of being stopped. A budget_exceeded refusal and the budget stop both name ciresk dev usage as the next step.
API
GET /v1/dev/usage returns the object above. It needs dev:policy.read and accepts users, agents and services. See the API reference at /api/dev.