shc move
The shc move command relocates stack deployments between nodes. This enables node cycling, infrastructure migrations, and maintenance scenarios.
Overview
Section titled “Overview”Move is designed for relocating stacks to different infrastructure, not for load balancing (use scaling and orchestrator-level scheduling for that).
Key Insight: Move = Backup + Uninstall + Recover. This reuses the proven backup infrastructure for data consistency and reliability.
| Operation | Flow | Original Stack |
|---|---|---|
| Move | Backup -> Uninstall -> Recover | Deleted first |
| Clone | Backup -> Recover | Kept |
shc move
Section titled “shc move”Rename or relocate a stack. Both the source and the target stack name are
required positional arguments (shc move <source> <target>); pass the same
name twice to keep the name and only change the node/environment/tenant.
# Rename a stack (same node)shc move postgres postgres-new
# Move a stack to a specific node (keep its name)shc move postgres postgres --node node-abc123
# Move with stack renameshc move postgres postgres-new --node node-abc123
# Move entire integration graph (all connected stacks)shc move nextcloud nextcloud --node node-abc123 --all-connections
# Move to a different environmentshc move postgres postgres --target-environment staging
# Cross-tenant move (requires system config permission)shc move postgres postgres --target-tenant other-org
# Dry run (show what would happen)shc move postgres postgres --node node-abc123 --dry-runArguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
<source> | Yes | Stack to move (positional) |
<target> | Yes | New stack name (same as source to keep it) |
| Flag | Default | Description |
|---|---|---|
--node, -n | — | Target node (optional, or swarm for Swarm) |
--target-environment | same as source | Target environment |
--target-tenant | same as source | Target tenant (requires system config permission) |
--all-connections, -a | false | Move entire integration graph |
--rename, -r | — | Rename dependencies: old:new,old2:new2 |
--dry-run | false | Show what would happen without executing |
--yes, -y | false | Skip the confirmation prompt |
--presumed-dead | false | Move off a DEAD node reusing surviving volumes |
--discard-local | false | With --presumed-dead: abandon local-only mounts |
Examples
Section titled “Examples”Basic Move
Section titled “Basic Move”# Move database to specific nodeshc move postgres postgres --node node-abc123
# Output:# Moving postgres -> node-abc123# v Backing up postgres# v Uninstalling postgres from node-xyz789# v Recovering postgres on node-abc123# Move completeMove to Swarm
Section titled “Move to Swarm”Move a stack to run as Swarm services (spans all Swarm nodes):
# Move to Swarm orchestrationshc move webapp webapp --node swarm
# The stack now runs as Swarm services with overlay networkingMove with Rename
Section titled “Move with Rename”Pass a different target name (the second positional) to rename during the move:
# Move and rename in one operationshc move postgres db-primary --node node-abc123
# Equivalent to:# 1. Backup postgres# 2. Uninstall postgres# 3. Recover as db-primary on node-abc123To rename dependencies of a connected move, use --rename with
comma-separated old:new pairs:
shc move nextcloud nextcloud --node node-abc123 --all-connections \ --rename postgres:postgres-2,keycloak:keycloak-2Move Integration Graph
Section titled “Move Integration Graph”When a stack has integrations, moving it alone would break connections:
# Example graph: keycloak -> postgres <- nextcloud# Moving postgres alone breaks both keycloak and nextcloudUse --all-connections to move the entire connected component:
# Moves postgres AND all stacks connected to itshc move postgres postgres --node node-abc123 --all-connections
# Output:# Moving 3 connected stacks to node-abc123# - postgres (primary)# - keycloak (consumer)# - nextcloud (consumer)# v Backing up all stacks (consistency_id: abc123)# v Uninstalling stacks in dependency order# v Recovering stacks on node-abc123# Move completeCross-Environment Move
Section titled “Cross-Environment Move”# Move from default to staging environmentshc move myapp myapp --node node-abc123 --target-environment stagingCross-Tenant Move
Section titled “Cross-Tenant Move”Cross-tenant moves require system-level configuration to enable:
# Move stack to different tenantshc move myapp myapp --node node-abc123 --target-tenant client-orgThis uses backup export/import internally to transfer data between tenants.
The Ingress Entry Point Cannot Be Moved
Section titled “The Ingress Entry Point Cannot Be Moved”shc move traefik traefik --node <other> is refused with
X400615CLNENT. This is deliberate, and the refusal is
not something to work around.
Traefik is the cluster’s single ingress: it binds :80/:443 on its
host, and in the default topology (no load balancer in front) every
managed DNS record — each deployment’s host, SHC_HOST, and every
system.apps.<name>.host — resolves to the public address of the node
Traefik currently runs on. Its ACME store and the served leaf
certificates materialize on that host too. A move relocates the
containers but performs no DNS re-point, so the records would keep
pointing at a node that no longer answers on :80/:443.
To relocate the entry point, reinstall it pinned to the target node:
shc install traefik -t master --node node-abc123The leader’s ingress reconcile (WatchSystemIngress) notices the
placement change and re-points every managed record itself. Two things
to check first:
- The target node must have a public address —
shc node update <node> --public-address <ip>— or the DNS ladder fails closed and registers nothing. - Expect a brief ingress interruption spanning the reconcile tick plus the DNS TTL.
A cluster fronted by a load balancer is unaffected by the node change:
when the Traefik deployment carries a conn@80/443 mapping, DNS follows
the VIP rather than the node. The refusal still fires — re-point the LB
backend instead.
Draining a node that hosts Traefik
Section titled “Draining a node that hosts Traefik”shc node drain relocates workloads through this same move path, so the
Traefik deployment is reported in the drain’s failed list with the
message above while every other stack migrates. Reinstall the entry
point on its new node, then re-run the drain.
Move Process
Section titled “Move Process”The move operation follows four phases:
1. Preflight Phase
Section titled “1. Preflight Phase”- Validate target node exists and is healthy
- Check quota availability on target
- Detect integration dependencies
- Require
--all-connectionsif stack has dependencies/dependants - Check for name collisions in target scope
- Parse
--renamedependency mappings (e.g.,postgres:postgres-new)
2. Backup Phase
Section titled “2. Backup Phase”- Create coordinated backup via
BackupOrchestrator - Generate shared
consistency_idfor all stacks - If
--all-connections: backup entire integration graph - For cross-tenant: export backup to target tenant
3. Uninstall Phase
Section titled “3. Uninstall Phase”- Uninstall stacks in dependency-safe order (dependants first)
- Clean up from source node
4. Recover Phase
Section titled “4. Recover Phase”- Deploy to target node
- Apply rename mappings
- Restore integrations automatically
- Verify all stacks healthy
Integration Handling
Section titled “Integration Handling”| Flag | Behavior |
|---|---|
| (none) | Move single stack; error if has connections |
--all-connections | Move entire integration graph atomically |
Constraints
Section titled “Constraints”| Constraint | Behavior |
|---|---|
--node optional | Required for cross-node moves; omit for same-node rename |
| Cross-tenant | Requires system-level config to enable |
| Name collision | Error if target scope has stack with same name |
| Integration dependencies | Requires --all-connections or error |
| Volume portability | DATA mounts transferred via backup/recover mechanism |
Error Examples
Section titled “Error Examples”$ shc move nextcloud nextcloud --node node-abc123Error: Stack 'nextcloud' has dependencies that would be orphaned: - postgres (provides: postgres-socket)
Use --all-connections to move the entire integration graph, orunplug dependencies first with: shc unplug nextcloud postgres-socket
$ shc move postgres postgres --node node-abc123Error: Target node 'node-abc123' already has a stack named 'postgres'.Use a different name: shc move postgres postgres-v2 --node node-abc123Backup Export/Import
Section titled “Backup Export/Import”For cross-tenant moves, backups are transferred via export/import:
# Manual equivalent of cross-tenant move:
# 1. Export backup to fileshc backup export --id abc123 --file /tmp/myapp.bak
# 2. Import backup into target tenantshc backup import --input /tmp/myapp.bak --tenant other-org
# 3. Recover from imported backupshc recover -f /tmp/myapp.bak --node node-abc123Dry Run
Section titled “Dry Run”Preview the move operation without executing:
$ shc move postgres postgres --node node-abc123 --dry-run
Dry run - no changes will be made
Move Plan: Source: postgres (node-xyz789) Target: node-abc123
Actions: 1. Backup postgres (estimated: 2.3 GB) 2. Uninstall postgres from node-xyz789 3. Recover postgres on node-abc123
Quota Impact: - node-abc123: +1000m CPU, +2GB memory - node-xyz789: -1000m CPU, -2GB memory
Use --dry-run=false to execute