Hosting: let users change their products — instance
Operator ask (Task 5722, verbatim): "user should be able to change his products: dedicated hosting can change to any other instance type and increase the storage to any other offered option. From billing prospective it means that the old product ended and the new started. But technically it is done without reprovisioning the machine. however some of the instance types might be incompatible, in that case ask the user for confirmation and reprovision" Follow-up from the operator: "Instance type change: no draining the node. change instantly."
Context: today a purchased hosting's instance type and storage are fixed at purchase. There is no resize or plan-change path:
- size is set only in RunInstances (`apps/api/app/aws/ec2.py:295-305`);
- hosting routes are cancel/undo-cancel/resume/delete only (`apps/api/app/routes/hostings.py:148-185`);
- storage options are discrete `rental_prices` rows of kind `storage` (`apps/api/app/hosting_catalog.py:189-199`), priced in `apps/api/app/billing/hostings.py:383-400,760-770`;
- launch passes `hosting.storage_gb` (`apps/api/app/hosting_provisioning.py:359-365`).
Proven technical recipe for storage (done by hand in Task 5722): EBS ModifyVolume, then live `growpart` + `resize2fs` on the host, with no reboot.
Requirements:
1. **Change product** action on a dedicated hosting (console + API). The user picks any other offered instance type and/or any LARGER offered storage option. Storage cannot shrink, because EBS cannot.
2. **Billing:** the old product ENDS and the new product STARTS at the change moment. On the subscription, end the old instance/storage items and start the new ones, following the existing billing rules (`apps/api/app/billing/__init__.py`, `docs/integrations/stripe.md`). Update the hosting's recorded `storage_gb` / instance type / price ids / `monthly_cents`.
3. **Storage increase:** done live, with no reprovision and no reboot. The CP calls ModifyVolume, and the node grows its partition and filesystem itself (growpart + resize2fs/xfs_growfs, via jaahd or the node helper). The new size is reported back to the CP.
4. **Instance type change:** instant, with NO draining. Stop the instance immediately, ModifyInstanceAttribute to the new type, start it, and the node rejoins. No reprovision, same instance id. Running sessions are interrupted. Say so in the dialog; don't wait for them.
5. **Incompatible types:** e.g. a CPU architecture change (arm64 ↔ x86_64) against the AMI, or other EC2 constraints. Detect these BEFORE acting, show the user that the change needs a reprovision (new machine; local state not carried over beyond what reprovision normally preserves), and proceed only on explicit confirmation. Then reprovision through the existing launch path.
6. Docs: update the hosting page `apps/docs/content/administration/plan-and-billing/hosting.md` (single-home).
Out of scope: shrinking storage. Shared (co-located) hosting tier changes. Customer-owned cloud connections (non-hosting cloud nodes). Auto-tagging and SSM instance profiles (Task 5725). Any existing hosting's record or billing reconciliation.
Done means:
- On a live hosting, increasing storage from the UI grows the volume and the filesystem with the node staying online. `df /` on the host shows the new size, and the subscription shows the old storage item ended and the new one started.
- Changing to a compatible instance type takes effect immediately with no drain wait, keeps the same instance id, and the node comes back online on the new type (`aws ec2 describe-instances`).
- Picking an incompatible type shows a confirmation that names the reprovision. Declining changes nothing; confirming reprovisions.
Subtasks
- [business_analysis] Hosting: let users change their products — instance Settled the "change hosting product" scope. It covers live storage growth, an instant in-place type change, and a confirmed reprovision for incompatible types, with billing that ends the old product and starts the new one prorated and invoiced now. The PRD has 20 acceptance criteria, each with a dev-environment oracle. Assumption: charge or credit immediately (DEC-4).
- [architecture] Hosting: let users change their products — instance hosting_changes row + plan-upgrade billing (preview, pending_if_incomplete, inverse-swap revert) + DBOS machine workflow + no-arg host.grow_root op. Children C1 EC2/verdict, C2 API/billing, C3 workflow, C4 node grow, C5 web, C6 docs, C7 dev e2e. Scope-downs: FU-4, FU-6.
- [plan] Hosting: let users change their products — instance Split on the architecture's C1–C7 boundaries: 7 coders in 4 phases (EC2+node grow → API/billing+web → workflow+docs → dev e2e). Critic MAJOR fixed (verdict ≤5 EC2 reads). Decided PL-1 pinned-AMI unavailable_reason, PL-2 no-changer guard.
- EC2 resize primitives and compatibility verdict Built 6 EC2 resize primitives + hosting_compat.verdict (≤5 reads, PL-1, D-L); full gate green, PR #4998 merged, Jaah Deploy success. Known limit: AWS returns only the latest volume modification, so the 4/24h branch rarely fires (C3 must map ModifyVolume rate limits).
- Node grows its own root filesystem Built host.grow_root (growpart then resize2fs/xfs_growfs, no args), converger + root report onto vnode_hosts (migration 0425), root_grow_wanted hook (default False). Full gate green; PR #4997 merged; Jaah Deploy succeeded.
- Change product API and billing engine Built hosting_changes + change API/billing engine (quote, one always_invoice swap, settle, revert, R6, interlocks); review findings fixed; full gate green; PR #5016 merged, Jaah Deploy 38157644046 success. Follow-ups named in report.
- Change product dialog and Machine field Built the Change product dialog (pick, quote, reprovision confirm, 3DS), the button, and the Machine field, change chip and 5s poll on the hosting page. Full gate green, #5004 merged, Jaah Deploy succeeded. Develop is red on its alembic head since #5003 (not from this PR).
- Durable hosting change machine workflow Built the durable hosting-change workflow (storage grow/stuck, in-place type with fallback, reprovision cut-over) and registered the changer; full gate green; PR #5028 merged, Jaah Deploy 38162090102 success. Follow-ups: stale tailnet devices on cut-over, FU-5.
- Document Change product on hosting page Documented Change product on the hosting page (actions row, Machine field, section with all six AC-6.1 topics); context + docs coverage pass. #5024 merged; Jaah Deploy 38161515657 success. Follow-up: stuck chip mislabelled "Resizing storage".
- Verify product changes end-to-end on dev Verified hosting product changes live on dev. Fixed 3 defects (PRs #5040, #5053, #5062), all merged and deployed. All oracles pass except AC-2.5, which lacks a Stripe key for dev's billing account. UX suggestions are in the report; coder can't file tasks.
- Hosting page: stuck change chip reads Stuck chip now reads "Change stuck" (storage label only at step volume_modified); test fails on old code, passes now. Quick gate green, visual check done, PR #5078 merged, Jaah Deploy green. Deferred: stopped machine still shows Active/Up since.
- Reprovision cut-over orphans the moved nodes' old Reprovision cut-over now deletes each moved vnode's old tailnet device after commit via the vnodes teardown path (best effort). New test covers delete + delete-fails; quick gate green; #5080 merged, Jaah Deploy succeeded.
- UX suggestions from the hosting product-change live test All 15 UX items shipped: #5088 (lane-merged) + #5103 (CI runners tab, after #5085). Quick gate green, review lanes clean, Jaah Deploy run 38184340477 succeeded. 8 further UX suggestions attached (coder can't file tasks).
Artifacts
Activity
- Created
- Updated
- Updated
- Runner started
- Ready
- Ready
- Ready
- Runner started
- Runner ended (done)
- Runner started
- Runner ended (done)
- Runner started
- Draft
- Draft
- Draft
- Draft
- Draft
- Draft
- Draft
- Ready
- Ready
- Ready
- Ready
- Ready
- Ready
- Ready
- Runner ended (done)
- Runner started
- Runner started
- Runner ended (done)
- Runner ended (done)
- Runner started
- Runner started
- Runner ended (done)
- Runner ended (done)
- Runner started
- Runner started
- Runner ended (done)
- Runner ended (done)
- Runner started
- Awaiting response
- Awaiting response
- Runner resumed
- Runner ended (done)
- Ready
- Ready
- Ready
- Runner started
- Runner started
- Runner started
- Runner ended (done)
- Runner ended (done)
- Runner ended (done)
- Runner ended (done)