Snapshots¶
A snapshot captures a stopped shed's rootfs as a named, immutable artifact. New sheds can be spawned from a snapshot, getting an independent copy of the captured rootfs and their own runtime mounts. Snapshots survive deletion of the source shed and are stored separately on disk.
Snapshots are useful for:
- Creating a configured baseline (e.g. an installed agent + dependencies) and spawning multiple experiment sheds from it.
- Capturing a known-good state before an experiment, then discarding the experiment shed and re-spawning from the snapshot.
What is captured¶
A snapshot is an offline (Tier 1) clone of the rootfs only:
- ✅ Captured: everything in the rootfs — installed packages, system config, home-directory dotfiles, files outside the workspace mount.
- ❌ Not captured: in-memory state (running processes, tmux sessions, page
cache); host-side mounts (
--local-dirworkspace contents, credential syncs). - ⚠️ If the source shed used
--local-dir, the workspace is mounted from the host, so its contents are not in the rootfs. The CLI surfaces this as a warning at create time.
Commands¶
# Create a snapshot from a stopped shed
shed snapshot create <shed-name> <snapshot-name> [--comment "..."]
# List snapshots on the current server
shed snapshot list
# Show details of a snapshot
shed snapshot info <snapshot-name>
# Delete a snapshot
shed snapshot delete <snapshot-name>
# Spawn a new shed from a snapshot (mutually exclusive with --image, --repo)
shed create <new-name> --from-snapshot <snapshot-name> [--local-dir ...]
Identity regeneration¶
Each spawned shed gets a fresh /etc/machine-id, fresh SSH host keys, and the
correct hostname.
- machine-id is handled by the rootfs itself:
/etc/machine-idis a symlink to/run/machine-id(tmpfs), andsystemd-machine-id-commit.serviceis masked. PID 1 generates a fresh UUID into/run/machine-idat every VM boot; nothing persists to disk. Each shed (fresh-create OR snapshot-spawn) gets a unique machine-id, with no host-side ext4 manipulation required. Note: this means/etc/machine-idregenerates on every boot of the same shed, not just the first boot. For shed's ephemeral test-environment workflow this is fine, but applications that key persistent state on machine-id and expect it to be stable across reboots will see a regression. - Hostname and SSH host keys are handled by a one-shot
shed-firstbootservice that runs early in boot — before D-Bus, journald, sshd, orshed-agentcache identity. firstboot writes/etc/hostnamefrom the kernel cmdlineshed.name=<name>value, then runsssh-keygen -Aso the new keys' comment field carries the spawn's hostname. It records the name in/var/lib/shed/identity.jsonand is idempotent — re-runs only when the recorded name doesn't match the cmdline value.
This makes ssh known_hosts and machine-id-based services work correctly
across snapshot-spawned sheds.
Constraints¶
| Constraint | Notes |
|---|---|
| Source shed must be stopped | shed snapshot create errors with a stop the shed first message otherwise. |
| Same backend only | A VZ snapshot can only spawn VZ sheds; a Firecracker snapshot only Firecracker. The CLI surfaces this as a clear error. |
--from-snapshot is mutually exclusive with --image and --repo |
The snapshot rootfs is the source of truth. --local-dir and credential mounts are still allowed because they are runtime mounts. |
| Snapshot rootfs is immutable | Stored mode 0444. Spawned sheds get a writable (0644) copy via reflink. |
--from-snapshot combined with --local-dir¶
These compose. --from-snapshot selects the rootfs; --local-dir is a
runtime VirtioFS / 9P mount that overlays the workspace path inside the VM.
Both can be set at the same time:
In that example the rootfs is the snapshot's (so installed tools / dotfiles
are present) but /workspace inside the VM is the host's /Users/me/proj,
not whatever the snapshot's rootfs had at /workspace. If the source shed
also used --local-dir, the snapshot's /workspace is whatever the rootfs
held before the local dir was first mounted — typically empty — so the
overlay behavior matches what you'd intuitively expect.
Storage layout¶
snapshots_dir defaults to ~/Library/Application Support/shed/vz/snapshots
(VZ) or /var/lib/shed/firecracker/snapshots (Firecracker). Override via the
snapshots_dir field in vz or firecracker server config blocks.
Snapshots show up in shed system df under their own section. They are
not removed by shed system prune — deletion is always explicit via
shed snapshot delete.
When the host filesystem supports reflink (APFS clonefile, XFS/Btrfs/ext4
FICLONE), the snapshot's rootfs.ext4 and any spawned shed's rootfs.ext4
share extents until they diverge. shed system df notes this so you don't
overcount on-disk usage.
Out of scope¶
- Live (memory state) snapshots — Tier 1 captures rootfs only.
- Snapshot export/import / multi-host transfer.
- Snapshot lineage chains — only the immediate
source_shedis recorded.
Known caveats¶
- machine-id is not stable across reboots of the same shed. Because
/etc/machine-idis a tmpfs symlink (see "Identity regeneration"), every VM boot generates a fresh value. This is the trade-off for unique-per-VM identity without host-side ext4 manipulation. For applications that expect a stable machine-id across reboots, recreate the shed instead of stop+starting it. - A snapshot create that crashes mid-write may leave an "invisible"
directory. If the host crashes between writing
rootfs.ext4and the atomic rename ofsnapshot.json, the directory undersnapshots_dirwill contain onlyrootfs.ext4and be filtered out ofshed snapshot list(which requiressnapshot.jsonto be present). Cleanup is manual for v1: remove the directory directly.shed system prunedoes not currently scansnapshots_dir.
Example: bootstrap, snapshot, experiment¶
# Set up a baseline shed
shed create base --image experimental
shed ssh base
# ...install your tools, customize the rootfs...
exit
shed stop base
# Snapshot
shed snapshot create base baseline-v1 --comment "agent + deps"
# Spawn an experiment shed from the snapshot
shed create experiment --from-snapshot baseline-v1
shed ssh experiment
# ...experiment freely; this shed is independent of the baseline...
exit
# Discard and try again
shed delete experiment
shed create experiment --from-snapshot baseline-v1