Hosting Resource Archival Guideline
Department
Summary
How to retire a hosting VM whose contract has expired and is not being renewed: snapshot the data, clean up the deploy controller, destroy the VM, remove DNS, then flip the registry record to Archived. Order matters — cleaning the deploy controller after the VM is destroyed is the most common foot-gun and silently leaves orphan state behind.
Table of Contents
| Section | |
|---|---|
| 1 | Summary |
| 2 | Purpose |
| 3 | Scope |
| 4 | Definitions |
| 5 | Pre-flight Checklist |
| 6 | Basis for Takedown |
| 7 | Step-by-Step Process |
| 8 | Verification |
| 9 | Rollback (Customer Comes Back) |
| 10 | Provider-Specific Steps |
Purpose
This guideline standardizes the cleanup of expired hosting resources so that snapshots are kept consistently, billing stops promptly, DNS doesn’t dangle, and the deploy controller doesn’t accumulate orphan entries that a future engineer has to clean by hand.
| Outcome |
|---|
Every expired hosting resource is either fully archived (snapshot kept, VM and dependent state removed, registry flipped to Archived) or explicitly left as Expired pending a renewal decision — never silently abandoned in a half-cleaned state. |
Scope
| # | In scope | Out of scope |
|---|---|---|
| 1 | Resource records of type Hosting with status Expired and confirmed non-renewal | Other Resource types (Domain Name, MA contracts, SSL certificates) |
| 2 | VMs at any cloud provider Kiluth uses | Customer-managed infrastructure outside Kiluth’s accounts |
| 3 | DNS records under kiluth.com and any other zone Kiluth manages | DNS records owned by the customer or third-party registrars |
| 4 | Deploy controller state tied to the destroyed VM | Application data already inside the snapshot |
Definitions
| # | Term | Meaning |
|---|---|---|
| 1 | Expired | Contract end date passed; cleanup hasn’t happened yet. VM usually still running and billing. |
| 2 | Archived | VM destroyed at the provider, snapshot retained. Default end state. |
| 3 | Deleted | VM and snapshot both gone. Rare — only when the customer explicitly asks for full data destruction. |
| 4 | Snapshot | A point-in-time block-level copy of the VM’s disk that can be restored to a fresh VM later. |
| 5 | Deploy controller | The system Kiluth uses to ship apps to the VM. May or may not have an entry for any given VM. |
| 6 | Registry | The Resource doctype in portal.kiluth.com, source of truth for which hosting resources exist and their status. |
Pre-flight Checklist
Confirm all of these before touching the provider console.
| # | Check |
|---|---|
| 1 | A documented basis for takedown exists — see Basis for takedown below. Record which basis applies on the Resource record. If renewal is still genuinely in play, leave the resource as Expired and revisit later. |
| 2 | The IP / identifier on the Resource record matches the VM you’re about to destroy. The Resource record’s identifier field holds the IP. |
| 3 | You have console access to the provider, the deploy controller (if applicable), the DNS provider, and portal.kiluth.com. |
| 4 | You have the provider reference runbook open for the provider and deploy controller in play (see Provider-Specific Steps). |
Basis for takedown
Either of these is a valid basis. Record on the Resource record which one applies, with dates.
| # | Basis | What must be true |
|---|---|---|
| 1 | Confirmed non-renewal | The PM and customer have explicitly confirmed they are not renewing. |
| 2 | Expiry with no response | The resource is past its expiry date, a documented number of written warnings were sent to the customer, no response was received, and the operator authorized takedown. |
Basis 2 exists because it is the common case. A customer who has stopped paying has usually also stopped replying, and waiting for a confirmation that will never arrive keeps dead infrastructure running and billing.
Do not write basis 2 up as though it were basis 1. Record what actually happened: expiry date, when each warning was sent and by whom, that no reply came, and who authorized the takedown. A future reader needs to see that silence was the basis, not agreement.
Step-by-Step Process
Tick each box on the Resource form’s Archival Checklist as you complete the corresponding step.
Step 1. Locate the VM at the Provider
Find the VM by its IP / identifier from the registry. Click into it to confirm:
| # | Cross-check |
|---|---|
| 1 | The VM’s project / tag matches the Resource’s project field. |
| 2 | The VM size (RAM / disk / region) is what you expect, so you can sanity-check the snapshot size later. |
| 3 | The VM is still running. If it’s been off for a while, decide whether the snapshot is still useful or whether you can skip straight to destroy. |
Step 2. Take a Snapshot
Use the provider’s snapshot/image action. Accept the default snapshot name when the provider auto-generates one — the provider reference runbook records the naming pattern Kiluth uses.
| # | Snapshot rule |
|---|---|
| 1 | Live snapshots are fine for expired non-production customer environments. |
| 2 | Power-off-then-snapshot is only required when the VM is actively serving writes you can’t afford to half-capture (e.g. a live database under load). |
| 3 | Wait for completion before moving on — the snapshot row should show a real size and a Created … minute ago timestamp, not a “Taking Snapshot” / “In Progress” indicator. |
✅ Tick Snapshot Taken on the Resource form. Paste the snapshot name/ID into Snapshot ID.
Step 3. Verify the Snapshot Independently
Visit the provider’s Snapshots / Images list (not just the VM’s own backups tab) and confirm the row exists with a real, non-zero size. Once the VM is destroyed, a missing snapshot means the data is unrecoverable — verify before going further.
Step 4. Clean the Deploy Controller (only if the VM is managed by one)
⚠️ Order matters — do this BEFORE destroying the VM. A deploy controller’s cascade-delete typically connects to the host to shut containers down gracefully. With the host alive that takes seconds; with the host already destroyed it can hang silently and remove nothing, leaving orphan state behind.
Skip this step entirely if the VM was never registered with a deploy controller.
If it was, remove its entries in this order:
| # | Object | Note |
|---|---|---|
| 1 | The server/host entry and everything it cascades to | App + databases + any supporting services in one action where the tool supports it |
| 2 | The now-empty environment for that customer/project | Delete after the server entry is gone |
| 3 | The now-empty project-level container holding that environment | Deploy controllers usually nest resources as project → environment → resources, with the project sitting outside the server. Deleting the server does not remove it. Delete it only once it holds no other environments still in use. |
Confirm nothing referencing the VM remains before moving on. Check the project-level container specifically — because it is not a child of the server, a server-first cascade leaves it behind, and an orphaned project is easy to miss.
Two ways to work through it. Both are correct. The warning above applies to either: whichever you pick, do it while the VM is still alive.
| Approach | How | When to use it |
|---|---|---|
| Server first (default) | Delete the server entry and let it cascade through the resources under it, then remove the environment and the project-level container. | Fewest actions. Good when you trust the tool’s cascade and the setup is routine. |
| Bottom-up | Delete in reverse nesting order: app, then databases and supporting services, then environment, then project, then server. | You confirm each object is gone before moving to the next, instead of trusting one cascade to have caught everything. Slower, but nothing is assumed. |
Bottom-up is Kiluth’s working practice and was used on the 2026-09-09 PROJ-0007 decom. Neither order is safer than the other; bottom-up simply makes each step verifiable.
Concrete steps per tool, and how to recover if the VM was destroyed first, are in the provider reference runbook maintained by Technology (kiluth-infra/runbooks/hosting-archival-provider-reference.md).
Step 5. Destroy the VM
Use the provider’s destroy/terminate action. Two universal rules at the destroy modal:
| # | Rule |
|---|---|
| 1 | Snapshot must NOT be deleted in the destroy modal. Most providers let the destroy action sweep snapshots if you opt in — leave that option unchecked. |
| 2 | Type the VM name to confirm. Most providers require this. |
Wait for the success toast. Confirm the VM no longer appears in the provider’s VM list.
✅ Tick VM Destroyed on the Resource form.
Step 6. Clean DNS
Open the DNS console. Search for records pointing to the VM’s IP in every zone the provider hosts — not only the Kiluth zone. Search by IP address, not only by the customer-app slug, so records that do not follow the naming convention still surface.
| # | Zone to check | Why |
|---|---|---|
| 1 | The Kiluth zone (kiluth.com) | Holds records on the <customer-app>.<env>.kiluth.com convention. |
| 2 | The customer’s own domain zone | A live site usually answers on the customer’s brand domain, and those records point at the same VM. They are the ones that matter most — a dangling record on a customer’s public domain is visible to their users. |
| 3 | Any other zone the provider hosts | Old project domains, demo domains, and redirects also point at VMs. |
| # | What to expect | What to do |
|---|---|---|
| 1 | An apex A record on the convention <customer-app>.<env>.kiluth.com | Delete it. |
| 2 | A wildcard A record (*.<customer-app>.<env>.kiluth.com) | Delete it. |
| 3 | Any related CNAME / TXT records | Delete or repoint, depending on whether they outlive the VM. |
| 4 | Records on the customer’s own domain pointing at the VM (apex, wildcard, or subdomain such as staging.<customer-domain>) | Delete or repoint. These do not follow the Kiluth naming convention, so a slug-based search will not find them. |
| 5 | Any record’s IP that does not match the destroyed VM | Leave alone — investigate before touching anything you didn’t expect to find. |
Most DNS consoles support bulk-select + delete. Always type-to-confirm if the console offers it.
Why this step lists zones explicitly. On 2026-09-09 a decom found that a resource archived on 2026-04-30, already ticked as DNS-cleaned, still had two live records on the customer’s domain pointing at a host destroyed four months earlier. The April cleanup checked the Kiluth zone and never opened the customer’s zone. Nothing was hijacked, but providers recycle IP addresses, and a dangling record can end up resolving to a stranger’s server.
Tick DNS Records Cleaned only once you have checked every zone, not just the Kiluth one.
✅ Tick DNS Records Cleaned on the Resource form.
Step 7. Flip Registry Status to Archived
On the Resource form in portal.kiluth.com, change Status from Expired → Archived and save.
The doctype’s _auto_set_status early-returns on terminal states (Archived, Deleted), so the value sticks even though before_save runs. The daily scheduler is idempotent on terminal states for the same reason.
If you prefer a one-liner from the browser console on the Resource page:
fetch('/api/method/frappe.client.set_value', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Frappe-CSRF-Token': frappe.csrf_token,
},
body: new URLSearchParams({
doctype: 'Resource',
name: 'RESOURCE-XXXXX',
fieldname: 'status',
value: 'Archived',
}),
}).then(r => r.json()).then(console.log);Verification
You should be able to assert all of the following before considering the archival complete.
| # | Check | Where to look |
|---|---|---|
| 1 | VM no longer appears in the provider’s VM list | Provider VM/instance list, all teams/projects |
| 2 | Snapshot row exists with the expected size | Provider Snapshots/Images list |
| 3 | Searching the destroyed VM’s IP returns zero rows in every zone the provider hosts, including the customer’s own domain | DNS console search, by IP rather than by slug |
| 4 | Deploy controller entries are gone — server/host, environment, and the project-level container (or flagged per Step 4 recovery callout) | Deploy controller’s server list and its project list |
| 5 | Resource record’s status is Archived | portal.kiluth.com Resource list filtered to Archived |
| 6 | All three Archival Checklist boxes are ticked and Snapshot ID is populated | The Resource form itself |
Rollback (Customer Comes Back)
Snapshots are retained indefinitely at the provider (cost: roughly used-disk-GB × the provider’s snapshot rate per month — small for typical app servers; check the provider reference runbook for current rates). To revive a customer:
| # | Step |
|---|---|
| 1 | At the provider: Snapshots → click the snapshot → Create VM (pick the same size and region). |
| 2 | At the DNS provider: re-create the apex + wildcard A records pointing at the new VM’s IP. |
| 3 | At the deploy controller (if applicable): re-add the new VM as a Server, then re-create the Environment + Application referencing it. |
| 4 | In the registry: create a fresh Resource record (don’t reuse the old one — that one is the historical archive). Link it to the same Project and Customer. |
Provider-Specific Steps
The process above is provider-agnostic on purpose. The concrete actions for each cloud provider and deploy controller — where the VM list lives, the snapshot action and naming, where to verify snapshots, the destroy flow, snapshot pricing, and recovery paths — are maintained by Technology in the provider reference runbook (kiluth-infra/runbooks/hosting-archival-provider-reference.md), not here.