Skip to content

Blog ·

Automating Hetzner Cloud billing and provisioning

Turning hourly Hetzner compute into a fixed monthly product: the calls behind each moment, the no-suspend problem, and four traps that only bite unattended.

By Provibr

Reselling Hetzner Cloud means turning something Hetzner bills by the hour into something you bill by the month, at a fixed price, on your own brand. An automated product built on it has the same three moments as any other:

  1. The order is paid. A machine has to exist, on the right plan, within seconds.
  2. The invoice is overdue. The machine has to go dark, and come back the moment it is paid.
  3. The subscription ends. The machine has to be removed, for real, so the meter stops.

Hetzner has calls for all three. What it does not have is anything that decides when to run them (that half is yours). And, unlike most providers, it does not have a suspend at all, which turns the middle moment into the interesting one. Everything below about the API was measured against the live Hetzner Cloud API while building the Provibr integration.

First, know how Hetzner bills, because your bill inherits it

Two properties of Hetzner's own billing shape every design decision downstream.

Hetzner bills cloud servers hourly, rounded up to full hours, but a server's bill never exceeds its monthly price cap; a server is billed for as long as it exists regardless of whether it is powered on or off, and billing stops only when the server is deleted.

A Cloud Primary IPv4 address costs €0.50 per month on top of the server price; a server without one is IPv6-only.

The first claim is the one that bites automation: a powered-off server still costs money. On your own hardware, "suspend" frees the resource. On Hetzner, a stopped machine is billed exactly like a running one until you delete it. So overage enforcement that powers a machine off does not stop your Hetzner cost; it only stops the customer using it. That is a genuine difference, and it belongs in your resale price.

The three moments and the calls under them

MomentCallSpeed
Paid orderPOST /servers with a server type and locationseconds; returns a running machine
OverduePOST /servers/{id}/actions/poweroff + lock the start controlseconds
Paid after allPOST /servers/{id}/actions/poweronseconds
CancellationDELETE /servers/{id}seconds; billing stops immediately
Reading stateGET /serversone call, all machines

Hetzner is fast where Plesk or InterServer are slow (a create returns a machine in seconds rather than minutes), so the timing pressure is the opposite one: things happen fast enough that a dropped response, not a slow one, is your real risk. Which is the first trap.

The overdue moment has no suspend

Hetzner has no suspend state, so "make this customer's machine go dark without deleting it" has exactly one implementation: power it off, and lock the start button until the overage is resolved. Provibr does precisely that. The machine stays off, the customer cannot restart it, and, because Hetzner still bills a stopped server, it keeps counting against the plan.

There is a subtle correctness requirement hiding in "lock the start button". The status poll runs every twenty seconds, and a legitimately stopped machine is a true observation. If the poll is allowed to overwrite the enforced state, the lock evaporates on the next round and the customer restarts a machine you switched off for non-payment. The enforcement therefore has to freeze the row while the overage stands, and the poll has to respect that freeze, which is easy to get wrong precisely because the "stopped" reading is genuinely true.

Four traps that only bite unattended

Each of these is harmless when you click it by hand and dangerous when a scheduler runs it at three in the morning. All four are measured.

A dropped response can double-order, and there is no idempotency key. POST /servers costs money and Hetzner offers no idempotency token, so a create whose response is lost looks, to a naive retry, like a create that never happened. Provibr guards it by attaching a label to every order carrying our own internal row id, and checking whether a machine with that label already exists before ordering again. Live-proven: two orders, one machine, the second recognised as already-placed. One honest consequence: a machine recovered this way reports that no root password was issued, because the password lived in the response that was lost and Hetzner will not hand it back.

progress: 100 sits happily next to status: "error". A poller that watches the progress counter and calls 100% "done" will report a failed create as a success. Progress is not a verdict; the status field is. We measured the two disagreeing on the same object.

GET /servers?status=off does not return a powered-off machine. We measured this three times. This one is vicious in a billing context specifically: your dunning powers machines off, so the set of "off" machines is exactly the set of suspended customers, and a reconciliation job that filters on status=off to find them finds none. Never filter the import or the reconciliation on status; read them all and compare.

A DELETE accepted mid-rebuild is a lie you both told. We issued a DELETE while a rebuild was in flight; Hetzner returned 200, and the rebuild afterwards still reported success. Two sources now disagree about a machine that no longer exists. If cancellation and reinstall can ever overlap in your scheduler, order them, and treat the machine's continued existence, not the action's success code, as the truth.

Metrics without draining the customer's rate budget

Usage graphs are live, and getting there was a rate-limit puzzle worth one paragraph, because the account's API budget is shared with the customer's own tooling and you do not want to starve it.

Hetzner's metrics API is a pull with a window, and consecutive windows join seamlessly (we measured a zero-second gap, a shared boundary point, identical values). Asking for type=cpu,disk,network returns all the series for one request. So one request every five minutes per server is 120 requests per hour for ten machines (about 3.3% of a token-bucket budget of one request per second), and it lands exactly on the panel's five-minute rollup. There is no memory series: the memory request returns 422, so that one graph is simply absent rather than faked.

Where your own invoice lives

The three moments above are half a product. The other half is what your bill looks like, and it does not come from Hetzner.

You pay Provibr for capacity, €0.20 per virtual machine per month, whether the machine is running or stopped. A Hetzner machine counts against that limit like any other. Because Hetzner has no suspend, an over-limit Hetzner machine is the one case where enforcement powers off rather than suspends: the machine keeps counting until it is deleted, and that is stated in the panel rather than hidden. See billing and licence for how capacity, overage grace and proformas work; the mechanism is identical across every provider, Hetzner included.

Over-limit is detected, not blocked at the instant it happens. You are told which resource and by how much and given a working day; if that lapses, the newest machines above the limit go off first, so what you built earliest survives. Nothing is switched back on for you when it is resolved: starting a machine is a decision, and the platform will not make it while nobody is watching.

What Provibr does, and what it deliberately does not

Provibr sits between your billing and your Hetzner project. A machine is an ordinary row: order it from a plan, power it, open a console, delete it. Enforcement powers off and locks rather than suspends, because that is all Hetzner offers. Every order is anchored so a dropped response cannot double-charge you. The agent dials outward from your own network, so nothing on Hetzner's side faces you and no inbound port is opened.

What it does not do: manage Hetzner firewalls (a firewall is a separate billed object, not a property of one machine), manage volumes, or draw a memory graph Hetzner will not measure. And it does not touch your billing relationship with Hetzner: you pay Hetzner for the compute on your own account, and the €0.20 you pay us is for managing it.

Hetzner billing behaviour and IPv4 pricing were read from docs.hetzner.com on 26 August 2026. The ordering guard, the progress/status disagreement, the status=off gap, the delete-during-rebuild behaviour and the metrics windowing were measured against the live Hetzner Cloud API while building the Provibr integration.

Start with one host and one agent

Create an account, install the agent with a single command and connect your first Proxmox host. The trial runs for 14 days and asks for no payment details.