Skip to main content

Guest image (kernel + rootfs)

The manager boots Firecracker microVMs from a kernel (vmlinux) plus a root filesystem (rootfs.ext4).

Storage Architecture

By default, run-dat-sheesh uses OverlayFS for efficient copy-on-write storage:

  • Base rootfs is shared read-only across all VMs (hard-linked)
  • Each VM gets a small overlay disk (sparse ext4) that stores only its writes
  • This enables near-instant VM creation (~50-100ms) and minimal disk usage per VM

See Architecture & Storage for details.

Build a guest image (Debian)

From the repo root:

./scripts/build-guest-image.sh

Outputs:

  • services/guest-image/dist/vmlinux
  • services/guest-image/dist/rootfs.ext4

There is also a convenience builder that produces both Debian + Alpine artifacts under ./dist/images/*:

make guest-images

What a guest image must have to work

At minimum, a compatible image must provide:

1) A working /sbin/init (PID 1)

This project uses a tiny C init (services/guest-image/init/guest-init.c) compiled into the rootfs as /sbin/init.

It is responsible for:

  • OverlayFS setup (when enabled):
    • Mount /dev/vda (base rootfs) read-only at /mnt/lower
    • Mount /dev/vdb (overlay disk) at /mnt/overlay
    • Create OverlayFS merge at /mnt/merged
    • pivot_root to the merged filesystem
  • mounting /proc, /sys, /dev
  • bringing up loopback (lo) so 127.0.0.1 works
  • starting the guest agent
  • starting a vsock -> TCP bridge via socat

2) Guest agent reachable on vsock port 8080

The manager speaks HTTP over vsock. In the default image:

  • guest agent listens on TCP 127.0.0.1:8080
  • socat VSOCK-LISTEN:8080,fork TCP:127.0.0.1:8080 bridges vsock to the agent

Guest agent endpoints include:

  • POST /exec
  • POST /run-ts
  • POST /files/upload (tar.gz)
  • GET /files/download (tar.gz)
  • POST /firewall/allowlist

3) User isolation model: uid/gid 1000 and /workspace

The guest image creates a user user with uid/gid 1000 and a writable workspace mapped at /workspace (backed by /home/user on disk).

Important behavior:

  • exec runs chrooted and treats /workspace as the only supported filesystem root for user operations
  • file upload/download is restricted to /workspace and uses tar.gz streams
  • symlinks and traversal are rejected

4) Toolchain inside the jail (BusyBox + staged tools)

Because exec runs inside a minimal chroot jail, a small toolchain is available there (BusyBox plus staged binaries like git, node, npm, tar, and deno).

5) Deno for run-ts

run-ts uses Deno and executes with restricted permissions:

  • --allow-read=/workspace (plus minimal /etc/* reads needed for DNS + TLS)
  • --allow-write=/workspace
  • optional --allow-net if requested (and still subject to firewall allowlist)
  • env vars: when the API request includes env: ["KEY=value", ...], run-ts will allow access to only those keys (so Deno.env.get("KEY") works)
  • structured return: scripts can call result.set(value) / result.error(err) and the manager will include result / error fields in the response JSON

6) Node.js for run-js

run-js uses Node.js and executes inside the same chroot jail as exec.

Behavior:

  • reads/writes are expected to stay under /workspace (same workspace contract as run-ts)
  • env vars can be passed via env: ["KEY=value", ...] and are available via process.env.KEY
  • structured return: scripts can call result.set(value) / result.error(err) and the manager will include result / error fields in the response JSON
  • stdout/stderr may include ANSI escape sequences (colors), which the Admin UI renders like a terminal

Uploading images to the manager

You can either:

  • set KERNEL_PATH + BASE_ROOTFS_PATH (for “built-in” base images inside the manager container), or
  • upload images via the Images API/UI (artifacts stored under IMAGES_DIR)