Skip to content
SHC Docs

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.

OperationSource lives after?Target locationTypical use
cloneyesnew name (possibly new node)branching, duplicating
moveno (uninstalled)new name or new noderelocate 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.


Every operation runs through the same eight phases in order. Each phase uses the ProgressTracker so the UI can render live progress.

Validates everything up front before committing any side effects:

  • Source stack exists (X404002APPNOF otherwise).
  • Target name is unique in the environment (X400123CLNMAN if not; _generate_unique_name can auto-pick a suffix if the user asked for --auto-rename).
  • Runtime is valid (_validate_runtime; X400608CLNRNT otherwise).
  • 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.

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.

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.

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.

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.Name and recompute the derived fields.
  • Update every IntegrationModel / ScheduleModel / BackupModel / BackupCacheEntry row 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 return X200008VLTREN).
  • Rename on-disk dirs: deploymentDir(old)deploymentDir(new), same for appDir, mountsDir.
  • docker rename the containers (or for swarm, recreate services with the new project name).
  • X200007RENAME on disk-rename failures.

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.

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.

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.


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:

  • 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.
  • 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. reinstallStack runs install → restore against the archive, ending with the source back at its pre-move location.
  • Delete the archive.
  • Metadata / filesystem renames are journaled in the CloneContext and 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 rename back.

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.


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:

  1. The connected-stacks walk. Stopping one stack without its consumers/providers leaves the cluster partially broken; clone handles it atomically.
  2. 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.go handles it, but CloneService is what tells restore the new namespace.
  3. 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.

EventWhen
A200601CLNSTRClone started
A200602CLNCMPClone completed
V400602CLNFALClone failed
A200603MVSTRMove started
A200604MVCMPMove completed
V400603MVFALMove failed
A200605RNSTRRename started
A200606RNCMPRename completed
V400605RNFALRename failed