A rented GPU can stop being useful in five structurally different ways, and the meter runs through all of them. Each way is caught by a separate watchdog with a narrow gate, because the checks that would catch the other four are exactly the checks that stay warm during this one.
A watchdog is a question asked on a timer, and the question fixes what it can possibly see. Ask “has this machine gone quiet?” and you never notice the one that was never noisy: with no first report to go stale from, its silence looks identical on the first tick and the thousandth. Ask “has it ever reported?” and you never notice the one that reported for an hour and then stopped. Neither question reaches an instance that exists at the provider but was never recorded here — both start from an inventory it is not in.
The tempting move is to merge them into one health check with one threshold. It fails in the expensive direction: a merged check has to be permissive enough not to destroy a machine that is legitimately in a slow phase, and at that threshold it stops catching the fast, cheap-to-detect failures the narrow checks were built for. Five questions, five gates, five cadences.
| How it died | What still looks fine | The net that sees it |
|---|---|---|
| Provisioning stalls before the machine ever registers itself | Nothing has gone stale — there is no report to age. | The provisioning deadline, fixed at the moment the request was placed. |
| The instance boots, but its start-up never completes | The provider reports a running instance, correctly. | The never-reported net, once the boot window has elapsed. |
| The worker process dies while the instance keeps running | Every check that talks to the provider succeeds. The instance really is up. | The went-silent net — and only that one. |
| The network path to the machine drops | The machine itself may be perfectly healthy behind the break. | The lost marker: it flags the machine and stops routing to it, and deliberately does not destroy. |
| An instance exists at the provider with nothing recorded here | Every net that starts from our own inventory, because it is not in it. | The orphan sweep, comparing the provider's list against ours. |
Idle machines are the largest avoidable line on a GPU bill, and the chain that handles them is the only one that calls a provider to turn something off. Every thirty seconds a short live utilisation sample decides one of two things about each running machine. If it is quiet, two timers are armed together: a stop timer and, behind it, a destroy timer. If any traffic was seen, both timers are cleared.
The clearing step is the one place in the chain with no condition on it. Everywhere else a gate that misfires fails safe: the worst outcome is a machine that keeps running and keeps costing money until someone notices. On the clearing path a gate can only fail dangerous. Make clearing conditional on the machine being in a particular recorded state, and a machine that has drifted out of that state — for any reason, including one nobody anticipated — can never clear its timers again. It keeps serving live traffic with a destroy timer counting down against it, and no amount of observed work helps, because the observation is never permitted to take effect. So the rule is blunt: traffic clears both timers, whatever the recorded state says.
Both timers are armed together and fall due at different times, so the reversible action always precedes the irreversible one — a stopped instance keeps its disk, a destroyed one is gone. The intent is recorded before either provider call goes out, and the component that continuously reconciles our inventory against the provider only acts on machines whose recorded intent is to be running, so a deliberate stop is never quietly undone by the next reconcile.
The destroy call itself is one line. Everything above it is refusals, and each refusal exists because the naive version of the check destroys something it should not. This is the order every machine-level reaper walks before it is allowed to act.
There are three independent ways to believe a machine is alive: the provider answers that the instance exists, our own control plane records a successful poll of it, and the worker on the machine reports for itself. Folding all three into one freshness value is the right answer to an operator's question — is anything at all still in contact with this machine? — and the machine-detail API returns exactly that, because on machines that register themselves one of the three channels is never written by anybody and would otherwise read permanently stale.
It is the wrong answer to the reaper's question, which is narrower: is the process that was supposed to be doing work still doing it? Only the worker's own report answers that. The other two channels are written by something other than the workload, so they stay warm in the exact failure the went-silent net exists for — the instance is up, the provider is happy to keep charging for it, and the thing being paid for has been dead for a quarter of an hour. Widen that gate to the composite and nothing ever destroys a wedged, billing machine again.
Thresholds follow the same logic. A machine that reported and then stopped gets a quarter of an hour, with a hard floor under that value so a misconfiguration cannot shrink it to something that kills healthy machines mid-request. A machine that never reported at all gets half an hour from first observation, because that window has to cover a cold image pull on a slow link. A machine that is merely unreachable is marked and taken out of rotation, never destroyed: the fault may be in the path rather than in the machine.
Read the third column as an audit: it is what the decision is actually made on. Five of these can destroy a machine; the last four cannot touch one at all, which is why they are allowed much cruder rules.
| Watchdog | Asks every | What its gate reads | What it can do |
|---|---|---|---|
| Idle scanner | 30 s | A short live utilisation sample | Arms or clears the stop and destroy timers |
| Auto-stop | 30 s | The stop timer, plus whether work is pinned to the machine | Stops the instance; the disk survives |
| Auto-destroy | 30 s | The destroy timer, same work gate, machine already stopped or idle | Destroys the instance |
| Boot failure | 60 s | Whether the machine has ever reported, against its boot window; start-up failure is re-confirmed live across ticks before acting | Destroys |
| Provision deadline | 60 s | The deadline fixed when the request was placed | Destroys |
| Went silent | 60 s | The worker's own report clock, and deliberately nothing else | Destroys |
| Lost detector | 60 s | All three liveness channels together | Marks the machine lost and stops routing to it |
| Orphan sweep | 5 min | The provider's instance list against our inventory | Destroys instances older than the grace window with nothing recorded here |
| Pool idle release | 15 min | A pool's own idle clock | Releases the pool; never touches a machine |
| Queue-wait timeout | 60 s | A queued request's age against its deadline | Fails the request |
| No-consumer detector | 60 s | Whether anything is listening for a pool's work | Fails the request rather than letting it wait forever |
| Stuck-dispatch reset | 60 s | A request's provisioning counter | Resets it so the request can be dispatched again |