I Deleted the Server. The Bill Kept Coming.
Deleting a cloud VM does not end the bill. The primary IPs attached to it keep charging on their own until the whole project is gone, and here is the teardown check I run now.
End of August, I finished moving a product off an old box onto a new host. The migration itself went fine. I deleted the VM, closed the tab, and went back to whatever I was doing before.
Then a charge showed up anyway.
€5.59 a month. Small enough that ignoring it would have been the rational move, except I could not work out where it was coming from. The server was gone. I had watched it go. Nothing showed under compute.
What was actually still billing
The IPs.
On most cloud hosts a primary IP is its own billable resource with its own lifecycle. Destroying the machine it was attached to does not release it. It sits there unattached, still yours, still charged, inside a project you have stopped looking at.
Deleting the server cleared one line item out of a container that had four things in it. There was the server, a snapshot I had taken as insurance before the migration, and two primary IPs. Each of those bills separately. It took deleting the entire project, all four, before the charge actually stopped.
I want to be careful about where the blame sits here. This is not a billing trick. Keeping the address alive after the machine dies is exactly the behaviour you want when you are rebuilding a box and need the IP to stay put, and roughly every provider I have used works this way for good reasons. The mismatch is that my mental model says “I deleted the server” while the billing model says “you still own four things”.
The invoice that lands after you are done
One more thing that will convince you the fix did not work.
An invoice arrived after I had deleted everything, and for a minute or two I assumed something was still alive somewhere. It was the previous month’s tail. Cloud billing runs in arrears, so the last invoice covers usage from before the teardown.
A charge that arrives after you delete is not evidence that something survived. Read the billing period on the line item before you go hunting. The invoice after that one is the one that tells you whether you actually finished.
The verification that happened first
Everything above is the entertaining part, in that it is a puzzle with an answer. The part that actually mattered happened before I deleted anything, and it is the part I watch people skip.
Before the old box went away:
- The archive was verified at 254MB, not just created. Size checked, md5 matched against the source.
- A restore dry-run ran clean, which is a different test from the archive being written without errors.
- The new host was confirmed as a strict superset of the old one. 42 of 42 collections present, nothing assumed to have carried across.
That last check is the one I trust most. Not a green log line from the migration script, and not “the app loads”. A count taken on both sides and compared, with the new side allowed to have more and never allowed to have less. It takes about ten minutes and it is the whole difference between deleting a server and deleting your only copy.
I think most migration disasters are not failures of the copy step. They are failures of the confidence step, where somebody looked at an exit code of zero and believed it.
The teardown check I run now
Whatever the provider, the shape holds:
- Verify the destination before touching the source. Count something on both sides. Rows, collections, buckets, files. The new side has to be a superset, and you have to have looked at the number.
- Restore the archive somewhere. Once. Even into a throwaway environment. Untested backups are decoration.
- List every resource in the project, not every server. IPs, snapshots, volumes, load balancers, DNS zones, object storage, machine images. Read the list off the dashboard or the CLI. Do not recall it from memory, because memory only stores the server.
- Delete the container, not the contents. If your provider groups things into a project or a resource group, deleting that is what ends the lifecycle. Removing items one at a time is how you leave one behind.
- Wait two billing cycles. The first invoice after teardown is the old month. The second one is the verdict.
- Then close the account or pull the payment method, if you are genuinely done with that provider.
None of this is clever. It is a checklist, and the reason to write a checklist down is that the failure was never technical. I knew IPs were billable resources. I have known that for years. I still deleted the server and assumed the job was finished, because “delete the server” is the shape my head stores the task in.
That gap between what you know and what you do when you are three tasks deep is where small recurring charges live, quietly, for as long as you let them. €5.59 a month is nothing. I still do not know how many months it would have run before I noticed, and my honest guess is more than I would enjoy saying out loud.