An owned host's hour cost

What an owned host's hour cost: stado cost host

$ stado cost host gpu-host --since-hours 24
gpu-host at 0.21 EUR/kWh (supplier invoice, residential tariff), since 2026-01-01T09:00:00Z
2026-01-01T09:00:00Z cpu busy 12%; GPU-1a… busy 100% 0.410 kWh, 0.380 above idle 30.000 W; GPU-2b… busy 0% 0.031 kWh, 0.000 above idle 31.000 W; 0.0926 EUR, 0.0798 EUR above idle
total 0.0926 EUR, 0.0798 EUR above idle, over 1 hour(s)

Every host that publishes its beacon on a period (stado serve --health-interval-seconds) samples each board's utilization.gpu and power.draw and its processor's busy share once per period, and keeps each hour of the last day in the beacon's hours block: per board UUID the samples, the busy samples and the watt-seconds drawn (each draw times the seconds since the board's previous sample), and the processor's busy samples. stado cost host TARGET [--since-hours N] [--json] reads that block from the host's newest beacon and prices each hour's energy at the tariff the registry declares for the host:

"power_tariff": {"price_per_kwh": 0.21, "currency": "EUR", "source": "supplier invoice, residential tariff",
                 "usd_rate": {"usd_per_unit": 1.08, "as_of": "2026-01-01", "source": "central bank reference rate"}}

set with stado registry set --path targets.<host>.power_tariff --value '…'. The registry refuses a tariff with no source (must name where the price was read …), a negative or missing price_per_kwh, an empty currency and any other field. Marketplaces state income in US dollars and the cost report is in US dollars, so a tariff in another currency carries usd_rate: the dollars one unit was worth, the date of that rate and the published table it was read from; the registry refuses a rate that is not above zero or lacks as_of or source. Without it the hour is priced in the tariff's own currency only, and every US-dollar figure (the cost report, stado market earnings, cost host's in USD line) says the tariff is in <code> and declares no usd_rate … instead of converting at a rate nobody stated.

A tariff that charges by the hour of day declares its bands and the time zone its hours are read in:

"power_tariff": {"price_per_kwh": 0.21, "currency": "EUR", "source": "supplier invoice, two-rate tariff",
                 "timezone": "Europe/Warsaw",
                 "bands": [{"from_hour": 22, "to_hour": 6, "price_per_kwh": 0.11},
                           {"from_hour": 13, "to_hour": 15, "price_per_kwh": 0.11}]}

A band runs from from_hour (inclusive) to to_hour (exclusive) of the local day, past midnight when to_hour is not above from_hour; an hour no band covers costs price_per_kwh. Every measured hour (cost host, stado market earnings, the cost report and placement) is priced at the band covering it, and cost host prints each hour's price_per_kwh. The registry refuses bands without an IANA timezone, a timezone without bands, an empty list, an hour outside 0 to 23, a band whose two hours are equal, a negative price, two bands covering the same hour, and any other band field.

The hardware's purchase is a cost of every hour too:

"hardware": {"purchase_price": 9000, "currency": "USD", "service_years": 4, "source": "purchase invoice"}

cost host prints the share of that price one hour carries (the price over the service years of 365 days of 24 hours), the wear over the window, and wear: not counted: <host> declares no hardware … for a host without it. The registry refuses a price or service life that is not above zero, an empty currency or source, and any other field.

A board turns the electricity it draws into heat in the room the host stands in. Where that heat replaces heating, the registry declares what one kWh of heat from the replaced heating costs (its fuel or electricity price over its efficiency) and, optionally, the months it heats:

"heat_use": {"price_per_kwh": 0.12, "currency": "USD", "source": "gas bill and boiler efficiency", "months": [1, 2, 3, 10, 11, 12]}

cost host then prints the boards' heat over the window in kWh, the part of it given off in the declared months (every month when months is absent), and its saving at that price. A host with no heat_use prints nothing about heat. The registry refuses a price not above zero, an empty currency or source, a months that is empty or names anything but 1 to 12, and any other field.

Each row prints the processor's busy share, each board's busy share and kWh, and the cost; an hour no board was sampled in is unpriced: no board was sampled, and an hour one of whose boards reported no energy is unpriced: board(s) <id> reported no watt_seconds this hour, never priced on the other boards' draw alone; a busy share whose samples were not counted is unread, never zero. Every reading an hour could not take is printed as unread: <cause> (boards: nvidia-smi exited …, processor: …).

A Mac's CPU, GPU and Neural Engine share one power domain, so on macOS the one board an hour prices is the package, macos-package: its draw is powermetrics' processor.combined_power and it is busy when the graphics sampler's gpu.idle_ratio is below one. powermetrics reads power only as root, so the host declares the privileged helper that prints one sample, host.power_helper (or STADO_HOST_POWER_HELPER), as command words, for example a sudoers-allowed ["sudo", "-n", "/usr/bin/powermetrics", "--samplers", "cpu_power,gpu_power", "-n", "1", "-i", "1000", "-f", "plist"]. Without it every hour of the Mac is unread: boards: host.power_helper (STADO_HOST_POWER_HELPER) is not declared … and unpriced; a helper that cannot run, exits non-zero, prints no plist or a sample without processor.combined_power or gpu.idle_ratio is named the same way.

A board draws power when nothing runs on it, so the whole draw overstates what a job or a rental cost. The beacon also keeps, per board and hour, the energy and seconds of its zero-utilization samples; cost host takes each board's idle draw in watts over the beacon's window, prints it, and prints the energy and cost above it — what work on the board added. A board whose idle draw was never measured in the window, or a beacon from before these fields, prints above idle unread: a board's idle draw was never measured instead of charging the whole draw as work.

--json prints stado.cost-host.v1 with the tariff, each hour (with kwh_above_idle, cost_above_idle, kwh_unread and each board's idle_watts), total_cost, total_cost_above_idle, hardware, wear, heat (use, kwh, kwh_replacing_heating, saving, or null) and unpriced_hours. Refusals: a host with no tariff (<host> declares no power_tariff, so its hours have no price; declare one with …), a host with no beacon, and a beacon with no hours block (its Stado predates host-hour measurement, or its beacon is published once).

Source: this website