Health checks¶
gpuprov health answers one question: is this VM still there and usable, or
has the vendor stopped, preempted, or reclaimed it? It is read-only, so it's
safe to run from cron, CI, or a monitor.
$ gpuprov health dev
dev HEALTHY aws us-west-2 i-0abc… ubuntu@54.190.1.2:22 (judged by vendor)
✓ record UP at us-west-2
✓ vendor running (i-0abc…)
! vendor spot instance (AWS may reclaim it with a 2-minute notice)
✓ ping 54.190.1.2 answers ICMP echo
✓ network 54.190.1.2:22 accepts connections
✓ ssh login ok as ubuntu
- gpu pass --deep
- k3s pass --deep
$ gpuprov health dev
dev GONE aws us-west-2 i-0abc… (judged by vendor)
✓ record UP at us-west-2
✗ vendor aws no longer has i-0abc… — terminated or reclaimed
✗ vendor terminated by AWS: spot capacity reclaimed
Usage¶
gpuprov health # every live VM
gpuprov health dev # by VM name
gpuprov health i-0abc123 # by instance id
gpuprov health 54.190.1.2 # by IP (a recorded VM's, or any address)
gpuprov health dev --deep # also nvidia-smi and k3s health
gpuprov health --json # machine-readable reports
| Exit code | Worst verdict |
|---|---|
| 0 | all healthy |
| 1 | degraded |
| 2 | unhealthy or gone |
| 3 | unknown |
Verdicts¶
| Verdict | Meaning |
|---|---|
| HEALTHY | running at the vendor and usable |
| DEGRADED | running, but unreachable, SSH or a deep check failing, replaced by another instance, moved to a new address, or flagged by the vendor (scheduled retirement, maintenance) |
| UNHEALTHY | the vendor reports it stopped, failed, or being deleted: typically a preemption, eviction, crash, or failed status check |
| GONE | the vendor no longer has it (terminated or reclaimed), or gpuprov down removed it |
| UNKNOWN | neither the vendor nor the network could tell |
How it decides¶
The check climbs a ladder. A later rung is skipped once an earlier one fails.
- record: gpuprov's local record (location, instance id, address).
- vendor: the cloud adapter's
diagnose. Every cloud reports whether the instance still exists and its normalized status. Clouds with richer health APIs add vendor-native findings, as the table below shows. - ping: ICMP echo. It's informational when the vendor answered, because clouds commonly block ICMP.
- network: a TCP connect to the SSH port.
- ssh: gpuprov's key logs in.
- gpu and k3s (
--deep):nvidia-smi -Lsees the GPUs, and the single-node cluster is healthy (node Ready, CoreDNS, GPU allocatable).
Fallback: generic IP checks. When the vendor can't be asked (an API or
credential error, or an IP address gpuprov has no record of), the verdict rests
on ping, TCP, and SSH alone. The report then says judged by network. Such a
check can confirm a VM is reachable and that gpuprov's key works. It cannot tell
a host that is merely down from one that was reclaimed, so a silent host is
UNKNOWN, not GONE.
Vendor-native signals¶
| Cloud | Signals beyond "exists + status" |
|---|---|
| AWS | why it stopped or was terminated (StateReason, e.g. "terminated by AWS: spot capacity reclaimed"), counting recently terminated instances; system and instance status checks (DescribeInstanceStatus); scheduled retirement, reboot, and maintenance events; spot lifecycle |
| GCP | preemption of spot/preemptible VMs (with lastStopTimestamp), upcoming maintenance windows, shutdowns in progress, status messages |
| Lambda | unhealthy (and since when it was last healthy) and preempted |
| DigitalOcean | powered-off Droplets (which keep billing) |
| Nebius | preempted (stopped) preemptible VMs, stopped VMs still holding quota and disk, maintenance events |
| Verda | spot evictions (discontinued) and notfound, failed installs and no_capacity, spot instances |
| others | existence and normalized status (the default) |
Adding signals for another cloud means overriding one method,
CloudProvider::diagnose. See Adding a cloud.
Library use¶
use gpu_provisioner::vm::health::{self, HealthOptions, SystemProbes, Target};
match health::resolve(&db, "dev")? {
Target::Vm(record) => {
let report = health::check(Some(&*provider), &record, &SystemProbes, HealthOptions::default()).await;
println!("{} is {}", report.name, report.verdict);
}
Target::Address(ip) => {
let report = health::check_address(&ip, 22, &SystemProbes).await;
println!("{ip} is {}", report.verdict);
}
}
HealthReport is serializable (it's what --json prints). The probes are a
trait (HealthProbes), so the check can run against simulated hosts in tests.