Deletion
A cancellation is reversible until the instance is terminated. After that the job expires every licence, releases the AWS resources (the IP after a 3-day hold), bills the last usage and queues the member account for closure within AWS's limits. Every site on the pool goes with it; the JetBackup copy in Wasabi and the 1Password item stay.
Cancellation
| End of billing period (default) | Immediate | |
|---|---|---|
| Request | DELETE /v1/hosting/{id}?action=delete_all&type=… → Dedicated::deleteHosting(); a cancel change row; status cancellation_pending | |
| WHMCS side | tblcancelrequests written now for the pool and every site on it, the unpaid renewal trimmed (existing CancellationRequests trait); WHMCS terminates on the due date through the module guard, which queues the job | no cancel request yet; the change stores execute_after = now + 24 h; dedicated:terminate-due (hourly) writes the request and dispatches the job when the window has passed |
| Abort window | until the due date | 24 hours (dedicated.abort_window_hours) |
| How to abort | {command: cancel_termination} or the "Cancel termination" button: deletes the cancel requests, status back to active, a cancel_termination change row. Refused once step 6 below has started. | |
| Sites on the pool | destroyed with the server. The cancel page lists every site on the pool and asks for explicit confirmation; the JetBackup copy in Wasabi is the only way back. Sites can be moved to Rochen One shared or another pool beforehand with the existing migration flow. | |
| Mails | confirmation with the termination date, the abort deadline and the list of sites that will be removed | |
Termination job
App\Jobs\TerminateDedicatedInstanceJob, same runner, lock and WAIT rules as deploymentterminate (normal: 3-day IP hold, final invoice) · abort (tearing down a failed deployment: IP released at once, no invoice; run automatically on final deployment failure)dedicated_instances.plan, replacing the deployment plan; resumable with dedicated:resumeterminate_instance. Everything before it can be undone by starting the instance againThe 10 steps
| # | Step | Does | Already done when… | Reversible |
|---|---|---|---|---|
| 1 | guard | status → terminating, lock taken, in-flight changes refused | — | yes |
| 2 | stop_instance | stop and WAIT stopped | state stopped | yes |
| 3 | final_backup | triggers a last JetBackup run to the Wasabi bucket and WAITs for it (skipped in abort mode). No EBS snapshots are taken: the Wasabi copy is the retained data | backup job reported complete | yes |
| 4 | licences_cancel | Manage2 expire, CLN delete keys (CloudLinux, Imunify360, KernelCare), JetLicense cancel, Softaculous licence cancelled (NOC API to confirm), MailChannels sub-account removed at cleanup; LiteSpeed → ticket | provider reports no active licence for the IP | re-issuable only |
| 5 | zabbix_remove | delete host, then the Zabbix IAM key, user and policy | not found | no |
| 6 | terminate_instance | TerminateInstances and WAIT terminated. Point of no return | state terminated | no |
| 7 | hold_eip | disassociate only; eip_release_after = now + 3 d. dedicated:release-ips releases it later; a redeploy inside the hold reuses it so DNS stays valid. Abort mode releases at once | address unassociated | yes, within the hold |
| 8 | delete_volumes | delete root and home volumes (the Wasabi backup is the retained copy) | not found | no |
| 9 | cleanup | key pair, SSM parameters under /rochen/dedicated/{hosting}/, security groups, MailChannels sub-account, Cloudflare DNS records, the pool's tblservers row. Kept: the 1Password item and the Wasabi bucket (the system has no delete permission there); a Maintenance ticket "Wasabi bucket to review" and a devops mail are raised | not found | no |
| 10 | close_billing | one transaction: terminate change applied (end of the timeline), last data-transfer pull, final usage invoice, pool and every Dedicated Site service on it Terminated, status closing_account, terminated_at, close_account_after = terminated_at + 3 d (after the IP hold; nothing else remains in the account) | status | — |
A failure after step 6 cannot be undone by a retry, so it opens a Maintenance ticket listing the steps left; the job stays resumable and support finishes it with dedicated:resume once the cause is fixed.
What is kept
Wasabi backups
The server's JetBackup bucket stays after deletion. The system has no Wasabi delete permission (Chris); deletion is a manual decision from the ticket and the devops mail the termination raises.
1Password item
The credentials item stays in the vault after deletion, for audit and for a restore.
Elastic IP for 3 days
Held unassociated so a client who changes their mind can be back online without a DNS change: dedicated:resume on a pool in the hold redeploys with the same IP and restores sites from the Wasabi backup.
Nothing in EC2
No EBS snapshots (MCS has none either). Volumes go with the instance; the member account closes once the IP hold has passed.
Closing the account
AWS allows closing at most 10 % of member accounts in any 30-day window, and a closed account stays suspended for 90 days before it disappears. Closure is therefore never a job step.
- Queueevery row in
closing_accountordered byclose_account_after. - Daily run
dedicated:close-accountsat 03:00: skip rows whose date has not come, confirm the IP was released, callCloseAccount. - Quota hiton
CLOSE_ACCOUNT_QUOTA_EXCEEDEDstop for the day, log a notice, incrementclose_account_attempts; alert devops after 30 attempts. - Success
account_closed_atset, statusclosed, aclose_accountchange row. The 90-day AWS grace period needs nothing from us.
States
stateDiagram-v2 active --> cancellation_pending: cancel (end of period or immediate) cancellation_pending --> active: cancel_termination cancellation_pending --> terminating: due date reached / 24 h window passed terminating --> terminating: steps 1–5 (reversible by support) terminating --> closing_account: step 6–10 done, final invoice closing_account --> closed: CloseAccount (quota-aware, daily) failed --> terminating: abort command (mode abort)
In the app
Cancel page
subscriptions/[id]/cancel gains the Dedicated case (it is empty for AWS today): type choice, reason, the list of sites that will be destroyed with a typed confirmation, and a plain statement of when the server will be destroyed and until when it can be stopped.
While pending
The server and subscription pages show a banner with the termination date and a "Cancel termination" button while cancellation_pending.
While terminating
The provisioning card shows the termination steps; controls are disabled.
After
The pool and its sites disappear from lists (Terminated); the final invoice appears under Billing.