shc / arch / where-it-lives ← lab docs

Nearly all of it lives under one directory

With the daemon running as root, everything shc keeps sits under the state home, /var/lib/shc. Six places inside it, one outside. Here is what you would lose if each one vanished — and then the one row that may not be on this box at all.

one box · one disk
/var/lib/shc state home · durable
  • shc.db every app, tenant and secret you have
  • deployments/<tenant>/<stack>/<env>/mounts/deployments/<tenant>/…/mounts/ your apps' actual files 3 homes ↓3 homes
  • backups/ every restore point you have taken
  • snapshots/control-plane/ rollback for the database above
  • certs/ the trust between your nodes
  • node.id which cluster this box belongs to
/etc/shc the one outside
  • config.yaml + config.d/*.yaml the settings you wrote by hand

/var/lib/shc the state home — six locations inside it, one outside at /etc/shc

One of those rows has three possible homes

The control plane above is pinned to this box. An app's mount is not. It can be a directory on this disk, a shared filesystem every node can read, or a real cloud disk that attaches to one node at a time and moves. Pick one and see where the bytes sit — and what a dead node costs you.

where the bytes actually are
/var/lib/shc/deployments/<tenant>/<stack>/<environment>/mounts/<name> A plain directory on this box's disk. The default — nothing to declare.
reachable from This node only. No other box can see these bytes.
if node-a dies The bytes go with the box. You restore from a backup, and you lose whatever happened since the last one.
how you get it Nothing. Every install lands here unless you say otherwise.
where the bytes actually are
/mnt/shc/nfs/<tenant>/<stack>/<environment>/mounts/<name> A shared filesystem mounted on every linked node. CephFS sits under /mnt/shc/cephfs/ instead.
reachable from Every node linked to the provider. They read and write the same directory.
if node-a dies The data was never on it. Another node already has the mount — unless the box that died is the NFS server itself.
how you get it Enable the managed singleton once, then install onto it.
where the bytes actually are
/var/lib/shc/deployments/<tenant>/<stack>/<environment>/mounts/<name> The same path — but it is a mountpoint now, not a directory. A real cloud disk is attached underneath it.
reachable from The one node it is attached to. rwo means one writer, and the disk moves rather than copies.
if node-a dies The disk outlives the box. You rebind it to a live node and the app comes back on the same bytes. No restore.
how you get it An x-shc-volumes block in the app's compose, plus a volume-capable connection for the cloud credentials.

Three buttons and the theme. Nothing on this page moves on its own.

the commands that touch it simulated
$shc storage ls --type local
/var/lib/shc/deployments/<tenant>/<stack>/<environment>/mounts/<name> a directory, on this disk
$shc backup restore --id <backup-id>
the only way back once the node is gone
$shc storage nfs enable --network 10.0.0.0/24
/mnt/shc/nfs/<tenant>/<stack>/<environment>/mounts/<name> mounted on every node
$shc install nextcloud --storage nfs
the app's requires.storage whitelist still has to allow it
$shc connection ls
hetzner the account token the disk is cut from
$shc volume ls
$shc volume rebind <volume-id> node-a node-b -y
how an app asks for a real disk compose
# in the app's compose file — the whole opt-in
x-shc-volumes:
  pgdata:
    # Ki/Mi/Gi/Ti, K/M/G/T, or a bare count
    size: 10Gi
    # rwo = one writer (default)
    # rox = read-only, many
    access: rwo

No x-shc-volumes key means an empty map and the plain host-bind path, byte for byte — that is the stated Phase-0 invariant in internal/core/volume/binding.go. So this block is the only thing standing between a directory and a real disk. An rwo volume attaches to exactly one node at a time; the protocol that keeps it that way when a node vanishes is its own page — volume fencing.

simulated — no daemon, no disk, no cloud API. Lines starting $ are real shc commands; lines starting name the path each one touches, not captured stdout. Node names are illustrative; the path shapes and commands are real.

These are the root paths, what you get when shcd runs as root. Run it as a normal user and the same tree moves to XDG: state to ~/.local/share/shc, config to ~/.config/shc. SHC_STATE_HOME overrides the root outright. Also under the state home but left off for clarity: staging/, tmp/, shcd.lock. There are exactly three storage-provider kinds — local, nfs, cephfs — and those three names are reserved for the managed singletons. A cloud block volume is a different axis: not a provider row but a per-mount disk, built from a named connection's credentials (hcloud-volume, aws-ebs-volume, gcepd, azure-disk, cinder-volume, dovolume, linode-volume, vultr-volume, cloudstack-volume). Verified against internal/core/paths/paths.go, internal/core/volume/binding.go, internal/daemon/volume_binder.go, internal/daemon/services_seams_volume.go, internal/modules/storage/models.go, internal/modules/storage/storageapi/types.go, internal/modules/storage/connbridge.go, internal/core/config/default.yaml, docs/guide/operations/storage-providers.md and docs/reference/cli/.