User guide: restoring a MachineSnapshot
How to get a usable disk back out of a MachineSnapshot, and why this is a
two-step flow rather than one "restore my VM" button.
Why there's no in-place restore
A Kubernetes PersistentVolumeClaim's spec.dataSource can't be changed
after the PVC is bound -- there is no such thing as "restore this existing
PVC back to an earlier snapshot" in the Kubernetes/CSI storage model.
Restoring always means creating a new PVC whose dataSource points
at the snapshot. That also means "restore" and "clone-from-snapshot" are
the same operation here -- there's no meaningful difference to model
separately.
Machine.spec.volumes is creation-time-only (same as spec.image/
spec.resources) -- editing it on an already-realized Machine has no
effect. So restoring into an existing running Machine isn't possible either;
what you actually get is a fresh PVC you point a new Machine at.
The two steps
1. Restore the snapshot into a new PVC:
kaironctl restore my-snapshot --target-claim db-restored-pvc
or as a CRD:
apiVersion: kairon.zyvor.dev/v1alpha1
kind: MachineSnapshotRestore
metadata:
name: db-restore-1
spec:
snapshotName: my-snapshot
targetClaimName: db-restored-pvc
# volumeName: root # required only if the snapshot covers more than one volume
# storageClassName: "" # empty uses the cluster default
# storageSize: "" # empty defaults to the VolumeSnapshot's own reported restoreSize
kairon-controller creates a real PersistentVolumeClaim named
db-restored-pvc with spec.dataSource pointing at the snapshot's
underlying VolumeSnapshot. Watch it with kaironctl get restores --
status.phase goes Pending → Succeeded once the PVC is Bound.
Under a WaitForFirstConsumer StorageClass (the common case, e.g.
Rancher's local-path-provisioner), the PVC stays Pending until step 2
gives it a consumer -- that's normal, not a failure.
Or from the dashboard: the Restores page lists every
MachineSnapshotRestore in the default namespace with its snapshot,
target claim, phase, and (once Succeeded) the restored claim name, and a
form creates a new one without needing kaironctl/kubectl for that one
action. The Snapshots page's own table grows a Restore button on
each row once that snapshot is readyToUse -- it jumps to the Restores
page with spec.snapshotName pre-filled, the same "prefill and switch
page" pattern the Snapshot button on the Machines page already uses
for creating a MachineSnapshot. A Delete button is also available on
each restore row -- deleting a MachineSnapshotRestore only ever removes
that bookkeeping object; the PersistentVolumeClaim it already created is
a normal, independent object once status.phase reaches Succeeded and
is never cascade-deleted with it (same posture kubectl delete on a
completed Job takes toward what the Job produced).
2. Point a new Machine at it (this is just the existing PVC-backed boot disk feature -- see docs/guides/machine-storage.md):
apiVersion: kairon.zyvor.dev/v1alpha1
kind: Machine
metadata:
name: db-restored
spec:
volumes:
- name: root
claimName: db-restored-pvc
resources: {cpu: "2", memory: "4Gi"}
runtime: {backend: qemu}
powerState: Running
Why this doesn't also create the Machine for you
The alternative -- MachineSnapshotRestore also constructing and creating a
full Machine object -- would mean either requiring the original Machine to
still exist (so its spec can be copied), which defeats the main real-world
use case (the original Machine is gone, that's why you're restoring), or
duplicating a whole Machine-spec templating scheme inside this CRD. Keeping
storage restoration and VM creation as two separate, already-solved steps
is simpler and works in the disaster-recovery case that actually matters.
Real limits today (v1 of this feature)
- One volume restored per
MachineSnapshotRestore-- for a Machine with multiple snapshotted volumes, create one restore object per volume you need back. - No admission-time validation beyond what the CRD schema enforces -- there's
no earlier warning at
kubectl applytime ifspec.snapshotNamepoints at aMachineSnapshotthat isn'tSucceeded/ready yet, or doesn't exist at all yet. Neither is treated as an immediate failure:status.phaseparks atPending(message names exactly what it's waiting on) and retries automatically every reconcile tick, the same "waiting on an external condition" posture step 1's own PVC-Pendingcase above already has -- so creating the restore slightly before its snapshot finishes, or even slightly before the snapshot object itself exists (both Machines and MachineSnapshots created together in one GitOps apply, ordering not guaranteed), just works once the snapshot catches up, no need to delete and recreate it. The "doesn't exist at all yet" case is bounded to a 30-second grace period (from the restore's owncreationTimestamp) -- long enough to absorb an ordinary apply-ordering race, short enough that a genuinely missing/misspelledspec.snapshotNamestill surfaces asFailedwithin a bounded, human-noticeable time rather than sittingPendingforever.status.phase: Failedis reserved for what actually can't resolve itself:spec.snapshotNamestill unresolved past that grace period, a missing/misspelledspec.targetClaimName, an ambiguous/unknownspec.volumeName, or a real PVC-creation error.