What it is
FUSE (Filesystem in Userspace) is a Linux kernel interface that lets an ordinary, unprivileged process implement a filesystem. Normally a filesystem driver lives in kernel space (ext4, xfs, and so on); FUSE instead exposes a small kernel module (/dev/fuse) that forwards every filesystem operation, an open, a read, a readdir, to a userspace program, which answers however it wants. The kernel doesn’t care what’s on the other end: a network share, a compressed archive, or in this case an object-storage bucket.
rclone is the userspace program on the other end here. It’s a general-purpose tool for moving files to and from dozens of storage backends, and one of its subcommands, rclone mount, uses FUSE to present a bucket as a real directory tree: ls, cat, and > all work as if the objects were local files, translated into S3 API calls behind the scenes.
Why sandkiln uses it here
Firecracker has novirtio-fs or 9p support at all. That’s an upstream constraint, not a choice this project made: there is no host-to-guest shared-directory mechanism to reach for, so a host-side bucket mount could never be exposed into the guest the way it might be in a container runtime. The only place a FUSE client can run is inside the guest, against the guest’s own kernel.
Given that, rclone was the pragmatic choice over writing a custom FUSE client: it already speaks S3-compatible APIs correctly, already has a mature FUSE backend, and ships as a single static binary that can be injected into a rootfs the same way the guest agent itself is (see images/inject-rclone.sh).
Key terms
- FUSE: the kernel/userspace interface described above. Requires
/dev/fuseto exist, which requires the guest kernel to be built withCONFIG_FUSE_FS(images/build-guest-kernel.sh) — Firecracker’s own default and CI kernel configs don’t enable it. fusermount3: a small setuid helper that lets a non-root user mount a FUSE filesystem without extra capabilities. rclone’s Linux FUSE backend always execs it internally to perform the actualmount(2)call, even when the calling process is already root — there’s no direct-mount code path that skips it. sandkiln’s guest agent runs as root (seeimages/inject-agent.sh’s systemd unit), so this isn’t for privilege elevation here, just because rclone always goes through it regardless of caller.- rclone remote: rclone’s own term for one configured storage backend (an endpoint plus credentials). sandkiln writes a minimal single-remote config file per mount, always named
sandkiln, alwaystype = s3, provider = Otherwith an explicitendpoint— generic enough for any S3-compatible store, not tied to one provider.
How it works in sandkiln
core/crates/daemon/src/routes_mounts.rs needed no new entry in the host-guest wire protocol at all. A mount is built entirely from operations sandkiln-protocol already has:
WriteFilewrites the rclone config (the endpoint and credentials) to a path under/root/.Chmodlocks it to0600before rclone ever runs, so nothing else on the guest (ps aux, another process reading/proc) can see the credentials — they’re in a file, never a command-line argument.Execrunsrclone mount <remote>:<bucket> <mount_path> --config <path> --daemon --daemon-wait=30s, backgrounding rclone’s own process so theExeccall itself returns once the mount is confirmed ready rather than blocking on a long-running foreground process.- A second
Execrunsmountpoint -q <path>to verify the result independently of rclone’s own exit code — if that disagrees, the daemon unmounts, removes the config, and reports a500rather than trusting a “success” that isn’t real.
Exec umount, then Exec rm -f on the config file. Nothing about a mount is re-applied on resume, fork, or a restored checkpoint — rclone is a live guest-side process, and Firecracker’s snapshot mechanism already captures a running process’s full state along with everything else in guest memory, so a resumed sandbox’s mount just keeps working with zero daemon involvement.
See it in action
This dev box’s test rootfs doesn’t have rclone injected (no real S3-compatible endpoint is configured here either — seeexamples/remote-storage-mount/README.md’s own caveat about that), so the honest example is the clean-failure path, which is itself a real, useful thing to see: a missing prerequisite reports as an ordinary 400 with rclone’s own error text, not a crash or an opaque 500.
rclone’s own binary not existing on this particular test image (see images/inject-rclone.sh), surfaced as-is rather than swallowed. A rootfs with rclone and fusermount3 actually injected, mounted against a real reachable S3-compatible endpoint, returns the 200 shape documented in Remote storage mounts.