Cloud Infrastructure / IaaS
The cloud infrastructure pricing pattern: customers consume resources by the hour (or minute, or second), get steep discounts when they commit to reserved capacity in advance, and operate across many regions and many instance types - each with its own published rate. The economic shape rewards customers who commit (predictable revenue), while still serving on-demand workloads at higher unit prices.
Real-world examples. AWS (EC2 hourly + Reserved Instances + Savings Plans + Spot), Hetzner Cloud (hourly with monthly cap), Linode, Vultr, Cloudflare Workers (per-request CPU-time), Fly.io (per-second compute), DigitalOcean Droplets, Scaleway. Common shape: published per-region per-instance-type rate card, three pricing options per resource (on-demand, 1-year reserved, 3-year reserved), per-second billing granularity, monthly cap on hourly accumulation, complex enterprise contracts overlaying everything for top accounts.
The shape of the problem
Cloud infrastructure billing combines almost every operational complexity at once:
- Catalog scale. A real cloud has 200+ instance types × 30+ regions × 3 commitment tiers = 18,000+ priced SKUs. Modeling each as a separate product is operationally untenable.
- Three commitment tiers per SKU. On-demand ("pay for what you use"), Reserved ("commit to 1-3 years for a steep discount"), Spot ("we can interrupt you anytime, deep discount"). The same physical resource flows through different billing paths depending on which contract the customer holds.
- Monthly cap on hourly accumulation. Hetzner's classic model: pay €0.012/hour for a CX22 instance, but cap the monthly bill at €4.51 (the equivalent of ~376 hours / a "monthly equivalent"). After the cap, additional hours that month are free.
- Reserved capacity that customers buy upfront. Customer pays $X today for the right to consume Y instance-hours of a specific SKU over 1-3 years. The reservation must be drawn down by matching usage events; unused reservation typically expires worthless or partially refundable.
- Enterprise contract overlays. Top customers negotiate global discounts, custom commitments, dedicated hardware pricing - which override the standard rate card across the entire account.
- Per-second billing granularity. Resource usage is measured in seconds; the invoice rounds to whatever granularity the SLA mandates. Aggregator behavior matters here.
Kontier blueprint
| Concern | Kontier primitive |
|---|---|
| 200+ instance types × 30+ regions | Keyed product per resource family, key = region_instance-type (e.g. us-east-1_m5.large) |
| On-demand pricing | One price per (product, price_key) |
| Rest-of-catalog fallback | One set price (is_set_price: true, no price_key) |
| Monthly cap on hourly accumulation | A STAIRCASE ladder whose last tier is free |
| Per-second granularity | Fractional quantity on usage events, snapped by the meter's quantity_rounding |
| Enterprise contract overlay | An agreed price stated on the customer's subscription lines, never a catalog edit |
| Reserved capacity | A fixed-length phase whose lines carry an allowance and an agreed price |
Build it
IDs in the examples are placeholders: use the id each create call returns.
1. Define the compute product
First a key set holding the SKUs, then the product linked to it:
Code
The product is keyed (it has a key_set_id) and metered (quantity_source: "METERED"): the meter owns the billed quantity, and each region + instance type SKU is one price_key drawn from the key set. Keys match [a-zA-Z0-9._-], 1 to 64 characters. A usage event or price carrying a key outside that set is rejected at the write path, which is exactly what you want for infrastructure - if the catalog doesn't have a SKU, the bill bounces loudly rather than silently losing revenue. Add SKUs with PUT /v1/key-sets/{id}/entries.
Bind a meter that sums instance-hours (see Meters):
Code
then POST /v1/products/{id}/meter-bindings with the meter's id and role: BILLABLE_QUANTITY.
2. Add prices for instance types and regions
The price key encodes both the region and the instance type, joined with _ (a key may not contain /). These are the product's own (BASE) prices, per instance-hour:
Code
For 18,000 SKUs, this is a script run from your inventory system - one POST per SKU. To avoid pricing every key, add one set price ("is_set_price": true, no price_key) that answers for every key without an exact price.
Prices land in the product's working draft. Publish the product to make them sellable:
Code
To roll a new rate card out at a set time, publish with "when": "at", an at wall clock and a zone; the scheduled version shows in the product's scheduled_versions until it fires.
3. Customer subscribes to on-demand pricing
Code
Code
Then start it with POST /v1/subscriptions/{id}/activate. On a keyed product, usage bills one line per key, and every line names a price_key. A deal therefore carries a line for each SKU the customer runs. Usage under a SKU the deal has no line for is accepted but billed by nothing. GET /v1/subscriptions/usage-without-line lists it per subscription, product and key. When a customer starts a new instance type, add its line: cut the live revision now, append a phase with the extra line, and publish. The renegotiation bills that key from its cut onwards.
4. Meter consumption
The infrastructure metering daemon emits events for every running instance, every aggregation window:
Code
Events are submitted with the instance ID plus the hour as the idempotency_key, so a duplicate submission counts once and each (instance, hour) counts separately. At this volume, send batches of up to 1,000 events ({ "events": [ … ] }).
5. Enterprise contract: 15% off compute for one customer
A rate agreed with one customer is not a catalog price: it is an agreed price stated on that customer's subscription lines. It carries forward to every later phase until something else is stated. To apply it from a date without drafting the revision yourself, author a subscription.transform change per SKU:
Code
The live revision is split at from, and the matching line bills the agreed rate from then on. For a contract that ends, use subscription.apply_for_a_while with for_count / for_unit or until instead; the agreed rate then stops holding at the end date. Preview either one first with POST /v1/changes/preview.
Variations
- Hetzner-style monthly cap. Give the SKU's price a STAIRCASE ladder whose last tier is free:
"tiers": [{ "up_to": "376", "unit_amount": "0.012" }, { "unit_amount": "0" }](on a product withpricing_model: STAIRCASE). The first 376 hours of the period cost €0.012 each, about €4.51; every hour beyond is billed at zero but still counted for analytics. - Reserved capacity. Sell a 1-year phase (
ends: { "kind": "duration", "duration": { "count": 1, "unit": "year" } }) whose line states anallowance({ "kind": "stated", "allowance": { "included": "8760", "rollover": false } }) and an agreed price for the commitment. Anexitrule withnot_beforeon that phase holds the customer to it; a cancellation inside it is refused withSUBSCRIPTION_EXIT_BLOCKEDwhen the rule'sgateisblock. - Spot pricing with dynamic rates. Compute the tier rate with a price formula and pass the current spot rate on each event in
pricing_vars, which binds named variables into the formula at finalize time. - Per-second billing. Send fractional quantities (
"quantity": "0.25"for 15 minutes); the meter sums them, and itsquantity_roundingsnaps the billed quantity to the increment your SLA mandates. - Egress / bandwidth. Add a separate keyed product for
network_bytes_egressedkeyed by source/destination region pair. Same pattern, different metric. - Storage with tiered IOPS. The storage product uses STAIRCASE tiers on provisioned IOPS, plus a separate metered product for accessed bytes.
What you don't have to build
- 18,000-SKU catalog as 18,000 product records (one keyed product replaces them)
- Per-second granularity reconciliation (fractional quantities, snapped by the meter's rounding)
- Enterprise discount overlay (agreed prices on the customer's lines, scheduled as changes)
- A report of usage nothing bills (
GET /v1/subscriptions/usage-without-line)
Next steps
- Key Sets - the multi-SKU catalog pattern
- Subscriptions - agreed prices, allowances and exit rules on a line
- Usage Metering - per-second event ingest
- Scheduling - transforms and changes that hold for a while