Persistent disks attached to nothing for a week or more
What does ZopNight detect here?
Persistent disks detached from every instance keep billing their full provisioned size. ZopNight requires the disk to be READY, referenced by no users list, outside Kubernetes management, and (when a detach timestamp exists) unattached for at least 7 days, then treats the whole monthly charge as recoverable.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-112 |
| Category | orphan |
| Severity | low |
| Metric | none — pure configuration read |
| Source | idle_disk.go |
Where it applies
Detached is not free
Deleting a VM does not delete its data disks unless someone set auto-delete, so every VM
teardown quietly mints candidates for this rule. A detached persistent disk bills its
full provisioned capacity. A 500 GB SSD disk costs the same attached to nothing as it
did serving production. Discovery of the fleet runs through Cloud Asset Inventory under
roles/cloudasset.viewer; note that the Cloud Asset API has to be enabled on the
project, or discovery silently returns an empty fleet rather than an error.
Four gates before a delete shows up
This rule ships with an unguided delete workflow behind it, so the predicate is the
entire safety margin, and it demands four things. The disk’s lifecycle status must be
READY (CREATING, RESTORING, FAILED, and DELETING states never qualify). The API’s users
list must be empty, which is the canonical “no instance mounts this” signal. The disk
must not be Kubernetes-managed. And when GCP recorded a lastDetachTimestamp, the disk
must have been unattached at least 7 days: a disk detached this morning is usually mid
VM-rebuild and about to be re-attached, not abandoned. Never-attached disks carry no
detach timestamp, so for them the empty-users gate stands alone.
The PVC trap
GKE persistent-volume backings and node disks can look unattached while a pod is rescheduling or a node drains, but their lifecycle belongs to the Kubernetes control plane. Deleting one from underneath a PVC is data loss wearing a cost-saving hat. The dedicated Kubernetes-managed gate excludes them all before any other logic runs.
Hunt for userless disks
gcloud compute disks list --filter="-users:*" \ --format="table(name,zone,sizeGb,type,lastDetachTimestamp)"Snapshot anything plausibly valuable, then delete; the remediation path uses
compute.disks.delete with compute.zoneOperations.get from
roles/compute.instanceAdmin.v1.
Snapshot first, delete second
Deleting recovers the disk’s full monthly charge; the recommendation’s optimized cost is $0. The rule abstains when pricing for the disk is unknown or zero, and every gate above fails closed, so an ambiguous disk simply never appears.