Skip to content

feat: support rootfs tar as --from source for VM driver sandboxes #2175

Description

@feloy

Problem Statement

openshell sandbox create --from currently accepts three kinds of input:

  • A Dockerfile (or directory containing one) — built into the local Docker daemon, tag passed to the gateway
  • A fully-qualified registry image reference — gateway's compute driver pulls it at sandbox creation time
  • A community sandbox shorthand — expanded to a registry reference, same pull path

There is no path for a pre-built rootfs tar archive — the flat filesystem tar produced by docker export, podman export, or buildah mount + tar.

This matters for air-gapped environments and CI/CD pipelines where a rootfs archive is the natural hand-off artifact, and where no container daemon (Docker or Podman) may be available on the host running the gateway. Today, passing --from ./rootfs.tar hits the error "local --from file is not a Dockerfile" because the existing file branch only recognises Dockerfile names.

Scope

This feature targets the VM compute driver only. The VM driver is the most coherent use case: it already consumes flat rootfs tars internally as part of its image pipeline, and it is the driver most likely to run in environments where no Docker or Podman daemon is available. Docker and Podman drivers consume image tags from their respective daemons and are not affected by this change.

Proposed Design

openshell sandbox create --from ./rootfs.tar       # flat rootfs tar
openshell sandbox create --from ./rootfs.tar.gz    # gzip-compressed rootfs tar

The rootfs tar is a flat filesystem archive — the kind produced by:

# docker export
docker export my-container > rootfs.tar

# podman export
podman export my-container > rootfs.tar

# buildah (no running daemon required)
ctr=$(buildah from "$TAG")
mnt=$(buildah mount "$ctr")
tar -cf rootfs.tar -C "$mnt" .
buildah umount "$ctr"

How it fits the existing architecture

The VM driver already consumes flat rootfs tars internally. Its image pipeline is:

  1. Obtain a container image (from local daemon or OCI registry)
  2. Flatten it to a rootfs tar (docker export on a throwaway container, or layer extraction)
  3. Extract the rootfs tar into a directory or guest VM
  4. Create an ext4 disk image from the extracted filesystem

A user-provided rootfs tar skips steps 1–2 entirely and feeds directly into step 3. No container daemon is required on the host — the tar is the artifact.

The flow for a rootfs tar:

  1. CLI detects .tar / .tar.gz / .tgz extension → ResolvedSource::RootfsTar { path }.
  2. CLI passes the file path to the gateway via driver_config in CreateSandboxRequest.
  3. The VM driver receives the path, copies the tar into its staging area as source-rootfs.tar, and feeds it into the existing rootfs extraction and ext4 disk creation pipeline.

No changes to the proto SandboxSpec.image field or to the gateway routing logic. The driver_config mechanism already exists for passing driver-specific parameters.

Constraints

  • VM driver only — the Docker and Podman drivers require image tags resolvable by their respective daemons. Supporting rootfs tar ingestion for those drivers is out of scope and would require a fundamentally different approach.
  • Local-gateway only — the CLI must be able to read the tar file and the gateway must be able to access it. Same locality constraint as Dockerfile sources.
  • Extension-based detection.tar, .tar.gz, and .tgz suffixes, consistent with how filename_looks_like_dockerfile works today. No magic-byte sniffing.
  • No daemon required — unlike the Dockerfile build path, this does not call any Docker or Podman API. The tar is consumed directly.

Alternatives Considered

  • Docker archive (layered, from docker save): requires either a container daemon to load via docker load, or implementing layer flattening with whiteout handling. The flat rootfs tar avoids both and matches what the VM driver already consumes internally.
  • Supporting all compute drivers: Docker and Podman drivers would still need a running daemon to make the image available. The daemon-free path only makes sense for the VM driver, which converts images to ext4 rootfs disks independently.
  • Stream archive bytes to the gateway over a new RPC: more complex, potentially supports remote gateways, but out of scope — remote Dockerfile sources are already unsupported.
  • OCI image layout archive (oci-archive:): a distinct layered format; out of scope here, can be a follow-up.
  • Magic-byte sniffing: more robust detection but breaks consistency with the existing filename-heuristic approach and adds complexity for no real gain.

Agent Investigation

Traced the VM driver's image pipeline:

  • crates/openshell-driver-vm/src/driver.rsexport_local_image_rootfs_to_path() produces a flat rootfs tar via docker export on a throwaway container. ensure_prepared_local_image_disk() and build_cached_local_image_rootfs_image() consume this tar.
  • crates/openshell-driver-vm/src/rootfs.rsextract_rootfs_archive_to() unpacks the rootfs tar using the Rust tar crate. create_rootfs_image_from_dir() creates an ext4 disk from the extracted directory using mke2fs.
  • crates/openshell-driver-vm/scripts/openshell-vm-sandbox-init.sh — guest-side extraction runs tar -xpf "$payload_dir/source-rootfs.tar" -C "$image_root".
  • crates/openshell-cli/src/run.rsresolve_from() classifies input; ResolvedSource enum needs a new RootfsTar variant.
  • proto/openshell.protoSandboxTemplate.driver_config (field 11, google.protobuf.Struct) can carry the tar path without schema changes.
  • crates/openshell-driver-vm/src/driver.rsVmSandboxDriverConfig needs a new optional field for the rootfs tar path.

Files likely affected:

  • crates/openshell-cli/src/run.rsresolve_from(), ResolvedSource, sandbox creation flow
  • crates/openshell-driver-vm/src/driver.rsVmSandboxDriverConfig, image preparation pipeline
  • architecture/ — CLI and VM driver doc updates

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

Labels

area:sandboxSandbox runtime and isolation work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions