Three problems, one procedure: growing a root disk, which is never resizable; replacing a machine whose guest you can no longer reach; moving off hardware that failed. Both machines exist at the cutover, so you can verify the new one before anything points at it.
It works because configuration is code you re-run and **data lives on volumes**, which detach from one VM and attach to another intact. The root disk does not survive: anything on it you have not scripted is gone.
# 1. create the replacement (bigger root, if that is why you are here).
# It comes up stopped, which is what lets step 2 happen before first boot.
POST /v1/vms {"name":"web2","ram_mb":4096,"disk_gb":80,
"image":"ubuntu-lts","ssh_keys":[...]} + Idempotency-Key
# 2. move the data — detach needs the VM stopped, so unmount in-guest first,
# and refuses a volume the old VM's snapshots include: delete those first
# (409 volume_in_snapshots names them)
POST /v1/vms/{vm_OLD}/actions {"type":"stop"}
POST /v1/volumes/{id}/actions {"type":"detach"}
POST /v1/volumes/{id}/actions {"type":"attach","vm_id":"vm_NEW"}
# 3. start the replacement, then re-run your configuration automation
POST /v1/vms/{vm_NEW}/actions {"type":"start"}
# 4. point DNS at the new VM's address (GET /v1/vms/{id} carries it)
# 5. verify, then delete the old VM. Its volumes
# left in step 2; delete any snapshots it still holds first, newest first,
# or this answers 409 vm_has_snapshots and names them.
DELETE /v1/vms/{vm_OLD}
The IP is bound to its VM for that VM's life and no endpoint reassigns one (why). So **put a DNS name in front of every VM** before you need this procedure, at a low TTL — 60s is plenty. With one, the cutover is a record change; without one, it is a search for everything that hard-coded the address.
If a specific address genuinely has to move, that is a ticket
(POST /v1/support/tickets), not a call.
volume_in_snapshots (409) — the old VM has a snapshot that includes the
volume you are detaching, and a snapshot restores a volume only on the VM
that took it. The snapshots extra names them; delete those first. Deleting
a snapshot leaves the volume's current data alone.vm_not_stopped (409) — detach needs the VM stopped. Unmount in-guest,
stop, detach.vm_has_snapshots / vm_has_volumes (409) — the delete in step 5 refuses
while the old machine still holds either, and lists what is left. The order
matters: snapshots first (newest first), then detach volumes, then delete.quota_exceeded (403) — both machines exist at once, so the project needs
headroom for both across the cutover.
Quotas.no_capacity_available (409) — no room for the replacement right now.
Nothing was changed, and raising your quota will not help.Both VMs bill while both exist — RAM only while each runs, root disk, address and snapshots regardless. Delete the old machine once the new one is verified; stopping it saves only the RAM line.