ReviewOS

also looking at this

stacks/ts-cloud

feat(fleet): move a deployed site to another server (#167 operation 2)

#176
Merged glennmichael123 wants to merge feat/site-move into main
4 files +951 -0
docs/cli.mdmodified+48-0
Changes to docs/cli.md
@@ -155,6 +155,7 @@ See [Preview environments](/features/preview-environments) for policy, source li
155155| `cloud server:ssh <name>` / `server:logs <name>` / `server:monitoring <name>` | Connect / logs / metrics. |
156156| `cloud server:deploy <name>` / `server:reboot` / `server:resize <name> <type>` | Deploy / reboot / resize. |
157157| `cloud server:rename <name> <new-name> [--apply]` | Rename a server in place. Prints the plan; `--apply` performs it. |
158| `cloud site:move <name> --to <server> [--apply]` | Move a deployed site to another server, DNS cutover included. |
158159| `cloud server:recipe <name> <recipe>` | Run a reusable script across servers. |
159160| `cloud server:worker:add/list/restart/remove` | Queue workers (Supervisor). |
160161| `cloud server:cron:add/list/remove` | Scheduled jobs / cron. |
@@ -165,6 +166,53 @@ See [Preview environments](/features/preview-environments) for policy, source li
165166
166167See [Laravel / Forge-style](/features/laravel) for the `infrastructure.compute` + `sites` config.
167168
169### Moving a site to another server
170
171Consolidation's second operation: three boxes with one app each becoming one box
172with three sites.
173
174```bash
175cloud site:move bughq --to statushq-box # plan only
176cloud site:move bughq --to statushq-box --apply # perform it
177```
178
179It moves the site's on-box footprint wholesale — the whole
180`/var/www/<slug>-<site>` tree (every release, `shared/`, the `current` symlink)
181plus the systemd units that run it. Those units are not regenerated, they are
182moved: rebuilding from the repo on the target would not be a move but a fresh
183deploy that happens to be preceded by a data copy, picking up whatever the repo
184says today rather than what is actually running.
185
186The order is chosen so that **every prefix of the plan is a working system**,
187on the old box or the new one:
188
189| Step | Why there |
190|---|---|
191| Stop background work on the source | A queue worker writing mid-`tar` produces a torn snapshot. The web service keeps serving the source is still live. |
192| Archive, carry, unpack, start | The target comes up but nothing routes to it yet. |
193| Health gate on the target's loopback | The public name still points at the source, so asking it would wave a broken target through. |
194| Route the site on the target | Same gateway builder the deploy uses, so a moved site is routed byte-identically to a deployed one. |
195| Cut DNS over | Only after the target has proved itself. A provider warning stops the run here rather than continuing to the drain. |
196| Drain the source | Units stopped and disabled, gateway fragment removed. **Files left in place.** |
197
198Nothing in the operation deletes anything, so a bad cutover is undone by starting
199the source's units again and pointing DNS back. That reversibility lasts until
200the source *server* is destroyed, which is a separate and deliberately separate
201command. Background units are enabled but not started on the target until the
202source is drained, so the two boxes can never both run a scheduler against one
203dataset.
204
205The archive travels through the machine running the command rather than directly
206between the boxes: a direct hop would need the target to hold a credential for
207the source, which is the same credential-radius problem consolidation already
208has. Both boxes must be enrolled with pinned host keys (`cloud server:validate`).
209
210Resuming works the same way as `server:rename` re-run the identical command and
211the finished steps skip themselves. Two steps deliberately never skip: the
212snapshot (an archive from an earlier attempt predates whatever the source has
213served since) and the health gate (a gate that remembers a previous pass is not a
214gate).
215
168216### Renaming a server
169217
170218A name is spelled in four places, and a rename is only done when all four agree: