SDK: Storage Snapshots¶
See also: SDK Overview, VFS Providers, Custom Images, Snapshots
VFS Providers¶
Gondolin can mount host-backed paths into the guest via programmable VFS providers.
See VFS Providers for the full provider reference and common
recipes (blocking /.env, hiding node_modules, read-only mounts, hooks, and
more).
Minimal example:
import { VM, RealFSProvider, MemoryProvider } from "@netrodev/gondolin";
const vm = await VM.create({
vfs: {
mounts: {
"/workspace": new RealFSProvider("/host/workspace"),
"/scratch": new MemoryProvider(),
},
},
});
Image Management¶
Guest images (kernel, initramfs, rootfs, and optional krun boot artifacts) are
resolved automatically from local overrides/store first, then from
builtin-image-registry.json when needed.
The default cache location is ~/.cache/gondolin/images/.
Override image selection / source:
# Use explicit local assets
export GONDOLIN_GUEST_DIR=/path/to/assets
# Change default image selector
export GONDOLIN_DEFAULT_IMAGE=alpine-base:1.0
# Override builtin registry URL
export GONDOLIN_IMAGE_REGISTRY_URL=https://example.invalid/my-registry.json
Build-id selectors (uuid) are resolved locally first and only downloaded from
the builtin registry when that registry has an explicit builds[buildId]
mapping.
Builtin registry entries are normalized: refs[name:tag][arch] stores a build
id, and builds[buildId] stores the downloadable source metadata (url,
optional sha256, optional arch).
Check asset status programmatically:
import {
hasGuestAssets,
ensureGuestAssets,
getAssetDirectory,
} from "@netrodev/gondolin";
console.log("Assets available:", hasGuestAssets());
console.log("Asset directory:", getAssetDirectory());
// Download if needed
const assets = await ensureGuestAssets();
console.log("Kernel:", assets.kernelPath);
To build custom images, see: Building Custom Images.
Use custom assets programmatically by pointing sandbox.imagePath at the
asset directory:
import { VM } from "@netrodev/gondolin";
const vm = await VM.create({
sandbox: {
imagePath: "./my-assets",
},
});
const result = await vm.exec("uname -a");
console.log("exitCode:", result.exitCode);
console.log("stdout:\n", result.stdout);
console.log("stderr:\n", result.stderr);
await vm.close();
Rootfs Modes¶
You can control rootfs write behavior per VM:
readonly: rootfs is read-only (EROFSon writes)memory: writable throwaway rootfs- on
qemu, this uses backend-native snapshot mode - on
krun, this is not RAM-backed; Gondolin creates a temporary qcow2 overlay on disk and deletes it on close
- on
cow: writable qcow2 copy-on-write overlay (default)- this does not write back into the original rootfs image
- by default it is a throwaway qcow2 overlay file that is deleted on close
- because it is a real qcow2 layer, it can be checkpointed
If the guest asset manifest.json contains runtimeDefaults.rootfsMode, that
value is used as the default when rootfs.mode is not provided.
Runtime Rootfs Size¶
Use rootfs.size to ensure the effective root disk has at least the requested
size without rebuilding the base image:
Gondolin grows the writable root disk image on the host with qemu-img resize
(no shrinking) and then runs resize2fs /dev/vda in the guest before
VM.start() completes.
The base rootfs image is not modified when using the default cow mode. When
combined with rootfs.mode="memory", Gondolin uses a temporary qcow2 overlay so
the guest-side filesystem resize survives for the lifetime of that VM.
The guest image must include resize2fs (Alpine package: e2fsprogs-extra). Newer
alpine-base images include it; custom images should add it to
alpine.rootfsPackages when using rootfs.size.
Scratch tmpfs Mounts¶
By default the guest /init mounts tmpfs over /root (mode 0700), /tmp,
/var/tmp, /var/cache and /var/log. Those paths live in guest RAM (each
capped by the kernel at half the VM memory) and are not part of disk
checkpoints.
Use the tmpfs option to replace that set. Each key is a guest path; size
(bytes, k/m/g suffix or a percentage of guest memory) and mode (octal
string) are optional:
const vm = await VM.create({
// only /tmp is RAM-backed and capped; everything else stays on the root disk
tmpfs: { "/tmp": { size: "256m" } },
rootfs: { size: "8G" },
});
Pass tmpfs: {} to disable all scratch tmpfs mounts. The paths are then
backed by the root disk, which is useful for cache-heavy workloads with little
VM memory, and their contents become part of disk checkpoints. Combine it with
rootfs.size since the default root disk has little free space. With
rootfs.mode="readonly" the root disk is not writable, so keep tmpfs mounts for
every path the workload writes to.
DEFAULT_GUEST_TMPFS exports the built-in set for extending it.
The setting is passed to /init on the kernel command line and requires a
guest image built with this Gondolin version or newer; older images ignore it
and always mount the built-in set.
Disk Checkpoints (qcow2)¶
Gondolin supports disk-only checkpoints of the VM root filesystem.
A checkpoint captures the VM's writable disk state and can be resumed cheaply using qcow2 backing files.
Backend support: checkpoints work with both
qemuandkrun. Resume enforces checkpoint backend-compatibility metadata. See VM Backends (QEMU vs krun).
See also: Snapshots.
import path from "node:path";
import { VM } from "@netrodev/gondolin";
const base = await VM.create();
// Install packages / write to the root filesystem...
await base.exec("apk add git");
await base.exec("echo hello > /etc/my-base-marker");
// Note: must be an absolute path
const checkpointPath = path.resolve("./dev-base.qcow2");
const checkpoint = await base.checkpoint(checkpointPath);
const task1 = await checkpoint.resume();
const task2 = await checkpoint.resume();
// Both VMs start from the same disk state and diverge independently
await task1.close();
await task2.close();
checkpoint.delete();
Notes:
- This is disk-only (no in-VM RAM/process restore)
- The checkpoint is a single
.qcow2file; metadata is stored as a JSON trailer (reload withVmCheckpoint.load(checkpointPath)) - Checkpoints require guest assets with a
manifest.jsonthat includes a deterministicbuildId(older assets withoutbuildIdcannot be snapshotted) - QEMU
rootfs.mode="memory"uses backend snapshot mode and is not checkpointable unless combined withrootfs.size; userootfs.mode="cow"when you need a writable qcow2 layer explicitly - Cross-backend resume (
qemu↔krun) requires guest assets with krun boot artifacts (manifest.assets.krunKernel) - Some guest paths are tmpfs-backed by default (eg.
/root,/tmp,/var/log); writes under those paths are not part of disk checkpoints (see Scratch tmpfs Mounts)
Debug Logging¶
See Debug Logging.