Flow: clone and move
Step-by-step trace of shc clone and shc move. Source of truth:
modules/clone/service.go’s
CloneService.
Both entry points share the same phase structure; what differs is which phases run and how the final stack relates to the source.
| Operation | Source lives after? | Target location | Typical use |
|---|---|---|---|
clone | yes | new name (possibly new node) | branching, duplicating |
move | no (uninstalled) | new name or new node | relocate across cluster |
When move targets the same tenant + environment + node and only
the name changes, CloneService takes a fast-path rename
(in-place DB rename, no compose down/up). Rename is internal — there
is no separate shc rename CLI command.
clone and move use a backup archive as the transport —
internally both run the backup flow against the
source, then the restore flow
against the target. rename is a pure-metadata operation (no data
movement) and takes a shortcut.
For a plain-English overview of responsibilities see the
clone module page; this doc is the procedural
trace.
Phases (shared)
Section titled “Phases (shared)”Every operation runs through the same eight phases in order. Each
phase uses the ProgressTracker so the UI can render live progress.
1. _preflight
Section titled “1. _preflight”Validates everything up front before committing any side effects:
- Source stack exists (
X404002APPNOFotherwise). - Target name is unique in the environment (
X400123CLNMANif not;_generate_unique_namecan auto-pick a suffix if the user asked for--auto-rename). - Runtime is valid (
_validate_runtime;X400608CLNRNTotherwise). - For
move, the target node is reachable and has capacity. - Every declared integration can be re-wired (plugs have matching sockets on the target).
- For
rename, checks whether the fast path (§5) is eligible.
2. _backup_phase
Section titled “2. _backup_phase”Take a full backup of the source stack into a temporary archive. This
is identical to shc backup create --output <tmp>.bak.
The archive contains volumes, converter outputs, vault secrets,
integrations metadata, port/host mappings, and vars.yaml.
For rename on the fast path (§5), this phase is skipped.
3. stopPhase
Section titled “3. stopPhase”Stop the source stack. Before stopping, GetConnectedStacks (from
app/graph.go) walks the
plug/socket graph to find every stack that could be affected —
consumers that would lose their integration, providers that the
source depends on — and stops them too if needed. The list is
returned so restartStoppedSources at the end knows which to
bring back.
4. uninstallPhase (only for move)
Section titled “4. uninstallPhase (only for move)”Run UninstallStack on the source. For move, the
source must go away — its ports, hosts, and vault namespace are
about to be reused by the target. For clone, this phase is skipped
so the source remains.
5. Fast path: isFastPathEligible
Section titled “5. Fast path: isFastPathEligible”For rename-only operations, if no volumes need to move (same node,
same storage provider) and no integration mapping changes, the fast
path replaces phases 2, 6-7 with a pure metadata rename:
- Update
DeploymentModel.Nameand recompute the derived fields. - Update every
IntegrationModel/ScheduleModel/BackupModel/BackupCacheEntryrow that references the old stack name. - Rename vault secrets:
tenants/<t>/stacks/<old>/...→tenants/<t>/stacks/<new>/...(via the vault provider’s atomic rename; failures returnX200008VLTREN). - Rename on-disk dirs:
deploymentDir(old)→deploymentDir(new), same forappDir,mountsDir. docker renamethe containers (or for swarm, recreate services with the new project name).X200007RENAMEon disk-rename failures.
6. Install target
Section titled “6. Install target”For clone and move, the target is created fresh via the normal
install pipeline, with variables lifted from the
backup manifest so integration references and user-supplied vars
survive the move. The new stack is installed with status=RESTORING
(not CREATED) to signal to other subsystems not to start until
restore completes.
7. Restore
Section titled “7. Restore”Run the restore flow against the temporary archive from phase 2 into the target stack. This copies volumes, re-imports vault, re-wires integrations under the target’s identity, and starts the stack.
On restore failure, raise X200009RESFAL.
8. restartStoppedSources
Section titled “8. restartStoppedSources”For stacks stopped in phase 3 that weren’t the source (consumers, providers), start them again. They’ll pick up the new integration targets automatically via the integration service’s startup resolution.
rollbackPhase / recoverPhase
Section titled “rollbackPhase / recoverPhase”If any phase from 2-7 fails, rollback runs. The contract is to leave the cluster in the most usable state possible, which differs by operation:
clone rollback
Section titled “clone rollback”- Delete the target stack if it was installed in phase 6.
- Delete the temporary backup archive.
restartStoppedSources(connected stacks stopped in phase 3).- Source is untouched — no uninstall was done.
move rollback
Section titled “move rollback”- Delete the target stack if it was partially installed.
- Re-install the source from the backup archive — the source was
uninstalled in phase 4 and the archive is the only copy of its
data.
reinstallStackruns install → restore against the archive, ending with the source back at its pre-move location. - Delete the archive.
rename rollback
Section titled “rename rollback”- Metadata / filesystem renames are journaled in the
CloneContextand undone in reverse order. - If the fast-path disk rename succeeded but a later step failed,
rename back. If container/service rename failed partway,
docker renameback.
In all three cases, if rollback itself fails, the stack ends with
StackStatus.ERROR and error_message carrying both the original
error and the rollback error — consistent with the update flow’s
double-failure behaviour.
Temporary files
Section titled “Temporary files”The intermediate backup archive lives under
paths.StagingDir + "/clone_<backup_id>". cleanupTempDir in a
defer block removes it regardless of success or failure. If
rollback needs the archive (e.g. to re-install the source), it runs
before the cleanup.
Why clone isn’t just “backup + restore manually”
Section titled “Why clone isn’t just “backup + restore manually””Three things are packaged inside CloneService that you’d otherwise
have to do by hand:
- The connected-stacks walk. Stopping one stack without its consumers/providers leaves the cluster partially broken; clone handles it atomically.
- Vault namespace remap. The backup manifest’s vault bundle is
keyed by the source stack name. Restore on top of a fresh target
name requires rewriting those keys;
restore_vault.gohandles it, butCloneServiceis what tells restore the new namespace. - The rollback that reinstalls the source from the backup. For
move, the source is gone after phase 4. The only safe rollback is to re-install it from the archive. Plain backup+restore tools don’t know to do this.
Events
Section titled “Events”| Event | When |
|---|---|
A200601CLNSTR | Clone started |
A200602CLNCMP | Clone completed |
V400602CLNFAL | Clone failed |
A200603MVSTR | Move started |
A200604MVCMP | Move completed |
V400603MVFAL | Move failed |
A200605RNSTR | Rename started |
A200606RNCMP | Rename completed |
V400605RNFAL | Rename failed |
See also
Section titled “See also”- Architecture: clone module
install.md— phase 6uninstall.md— phase 4 (move only)backup-restore.md— phase 2 + 7