Skip to content
SHC Docs

Flow: app uninstall

Step-by-step trace of shc uninstall <stack>. Source of truth: subsystems/lifecycle/lifecycle.go’s UninstallStack. For orchestrated multi-stack uninstall (e.g. during clone/move or tenant teardown), see UninstallStacksOrdered in the same file, summarized at the end.

Uninstall is not transactional. Unlike install (which rolls back on failure) and update (which reverts from a snapshot), uninstall uses a best-effort forward pass: every step is individually guarded, a failure in one step is logged but never blocks later steps. This is by design — “user said delete this” should make progress even when parts of the system are already broken.


func UninstallStack(
service *StackService,
name string,
environment string, // default: "default"
removeData bool, // if true, also remove the deployment dir
operationID string,
) error
  • GetStack(name, environment)X404906STKNOF if absent.
  • configure(stack) loads per-stack config overlays into the StackService so subsequent compose invocations see the right storage / vault / DNS providers.

A deployment with a non-null ParentDeploymentID is a VApp. This affects a few later steps (no network removal, special unintegration path).

For non-VApp stacks, read the rendered compose file (if any) and extract CPUMillicores + MemoryBytes so the usage counter decrement at step 14 uses the same numbers the install/update increments used. Wrapped in error handling — if the compose file is missing or malformed, proceed with zeros rather than refusing to uninstall.

LoadAppDefinition(appDir) — needed so later steps know which hooks, unintegration jobs, and integrations the app declared. For VApps, read from the parent stack’s appDir.

For each host allocated to this stack (GetAllocatedHosts), ask the stack’s DNS provider to remove the record. Failures inside deleteDNSRecord are logged but don’t abort — you might be deleting a stack whose DNS provider is offline.

User-defined pre_uninstall hook. Unlike install, a hook failure does not abort uninstall — runHookSafe logs and returns.

If this is a VApp, run runVAppUnintegration which undoes the changes the VApp made to its parent stack (e.g. the acme VApp removes its Traefik config). Failures are logged as X200003OPFAIL; a transaction rollback is attempted; the stack row is re-fetched so the next step has a clean session.

8. Unregister jobs + delete schedules (lines 134-135)

Section titled “8. Unregister jobs + delete schedules (lines 134-135)”
  • UnregisterStackJobs — remove this stack’s jobs from the runtime job registry.
  • DeleteSchedules — remove every row in ScheduleModel that references this stack.

Neither is wrapped in error handling here; if they fail, the whole uninstall aborts (but the stack is still marked in the DB so a retry works).

ComposeDown with:

  • RemoveVolumes=removeData — only wipe volumes when the user passed --remove-data.
  • RemoveOrphans=true — always.

A failure is logged as X200905CMPDWN but does not abort — the user asked to uninstall, and the next steps (network cleanup, integration unwinding, DB row deletion) must still happen or the stack becomes a zombie.

10. Deployment network removal (lines 156-171)

Section titled “10. Deployment network removal (lines 156-171)”

For non-VApp stacks, call RemoveDeploymentNetwork to remove the per-stack Docker network. The tenant-wide overlay is not removed — other stacks in the tenant still need it.

Failure is logged as X200906NETCLN and does not abort.

Same semantics as pre_uninstallrunHookSafe, failure logged.

For every IntegrationModel row where this stack is either the provider or the consumer:

  1. Run the declared unintegration jobs (runUnintegrationJobs).
  2. Delete the integration row.

Each step is guarded; a failure is logged as X200003OPFAIL with an operation tag identifying which step, a best-effort transaction rollback is attempted, and a needsStackRefresh flag is set so the next DB read gets a fresh session. After the loop, one db.Save() persists any successful deletes; failure here is also logged but not fatal.

db.Delete(&stack). This is the one step where failure aborts — if we can’t delete the row, return the error so the caller knows the stack is still nominally installed.

Only if removeData=true. Failure logged as X200049RMTREE; the DB row is already gone so the stack doesn’t reappear.

15. Usage counter decrement (lines 249-258)

Section titled “15. Usage counter decrement (lines 249-258)”

UpdateUsageOnStackDeletion drops the tenant’s CPU / memory / disk usage counters by the numbers from step 3. VApps are counted separately (isVApp=isVApp).


UninstallStacksOrdered is used when multiple stacks need to come down as a group (e.g. during clone/move, or tenant teardown). Key properties:

  • Topological ordering: ComputeUninstallOrder (from daemon/clone_adapters.go, ce module — the reverse of ComputeInstallOrder) returns a list respecting plug/socket dependencies. Consumer stacks come down before the providers they depend on.
  • Per-stack best-effort: each UninstallStack call is wrapped in its own error handling. A failure is captured in UninstallResult (with the error message), logged as A400401STKUOF, and the loop continues to the next stack.
  • Summary event: at the end, A200402STKUOC carries SuccessCount and TotalCount.
StepEventMeaning
Single uninstall successA200400STKUNSStack uninstalled
Ordered: per-stack successA200401STKUNOStack N of M uninstalled
Ordered: per-stack failureA400401STKUOFStack N of M failed
Ordered: final summaryA200402STKUOCN of M succeeded
Compose-down failure (non-fatal)X200905CMPDWNlogged
Network-remove failure (non-fatal)X200906NETCLNlogged
Rmtree failure (non-fatal)X200049RMTREElogged
Any guarded sub-step failureX200003OPFAIL with operation taglogged
InstallUpdateUninstall
Failure strategyRollback (reverse-order cleanup)Revert (restore snapshot)Best-effort forward
What happens on mid-flight failureStack rolled back, user sees original errorStack reverted to pre-update stateLogs accumulate; uninstall proceeds
What can leave zombiesNothing (unless --no-rollback)Nothing (revert itself can fail → StackStatus.ERROR)External DNS, external vault secrets, hook side-effects