07 / Image pipeline

Package with Docker. Boot with Linux.

A Docker image is a convenient root-filesystem recipe. kube-vm's build-disk target exports that filesystem into a raw ext4 image; the VM still needs its own kernel and an init process.

Flatten files, not a running container

The build creates a stopped container from the selected image and exports its filesystem as a tar stream. No container workload needs to run. The VM later boots that userspace under its own Linux kernel, not the Docker host's kernel.

Docker root filesystem to raw VM disk An optional Dockerfile builds an image. A stopped container is exported into a loop-mounted sparse ext4 disk. The disk is synced, unmounted, checked, and attached to a VM with a separate kernel. Dockerfileoptional image build Docker imagedocker create Export rootfstar stream Raw ext4 imageno partition table VM root disk+ separate bzImage sparse file → mkfsloop mount → extract
  1. DOCKER_TAG names the source image. If DOCKERFILE is set, first build it from the repository-root context. Create a temporary stopped container from that image.
  2. docker export provides the container's root filesystem, not image-layer history, runtime metadata, or the contents of mounted volumes.
  3. Create a sparse file, format ext4 with lazy initialization disabled, loop-mount it, and extract the tar stream into its root.
  4. Sync, unmount, run e2fsck, and remove the temporary container. Attach the resulting image directly or use it as an immutable CoW base.
Docker is the build-time packaging tool. There is no Docker daemon required inside the resulting VM unless your own workload needs one.

Build on a disposable Linux workspace

You need Docker, sudo, loop-mount support and ext4 tools (mkfs.ext4, e2fsck). Run from the repository root. Create the output directory and a dedicated, empty mount point first; the target does not do that for you.

mkdir -p ./disks ./rootfs-mount
make build-disk \
  DOCKER_TAG=kube-vm-rootfs \
  DOCKERFILE=./Dockerfiles/Dockerfile_ubuntu \
  DISK_DIR=./disks \
  DISK_IMG=base.img \
  DISK_SIZE=2500M \
  MOUNT_DIR="$(pwd)/rootfs-mount"

Omit DOCKERFILE to use an image already available to Docker. Defaults are DISK_DIR=disks, DISK_IMG=kube-vm-disk.img, DISK_SIZE=2500M and MOUNT_DIR=/mnt. A sparse file has the requested logical capacity without eagerly allocating all of its host blocks; populated files and metadata consume real space.

Protect existing data. This target formats the output path and mounts onto the chosen directory. Use a new image filename and an unused mount point, never a live VM disk. If a build fails, check mounts and the temporary container before retrying; the recipe does not install a cleanup trap.

A container rootfs is not automatically bootable

Supply init and drivers deliberately

Docker's CMD and ENTRYPOINT metadata are not executed by Linux when it mounts this disk. The root filesystem must provide an init process, such as /sbin/init, or you must specify a suitable init= argument. Likewise, guest networking and SSH must be configured as guest services.

The included Ubuntu Dockerfile installs systemd and SSH; the Kubernetes variant adds cluster tooling. These are starting points for experiments, not hardened images. Review account credentials and privilege settings before any network exposure.

Whole disk means /dev/vda

The target formats the entire file as ext4. There is no partition table, EFI partition or bootloader. For the first attached virtio block disk use root=/dev/vda, not /dev/vda1. Attach additional devices deliberately; changing their order can change Linux device names.

./bin/kube-vm -server=false \
  -kernel ./bzImage \
  -cow-block ./disks/base.img=./disks/test.overlay \
  -cmdline "console=hvc0 root=/dev/vda rw init=/sbin/init"

This no-initrd example requires the kernel to have virtio-MMIO, virtio block, the chosen console and ext4 built in. If those are modules, provide an initrd that loads them before mounting root. The kernel must also be compatible with the image's userspace architecture.

Build once, give each VM a private future

Once the base is checked and unmounted, keep it unchanged and use a different CoW overlay for each VM. Docker layers are flattened during export; kube-vm's overlay is a separate block-level mechanism, not Docker's filesystem layer stack.

If you later need the running machine's memory and execution state too, take a full checkpoint. A root disk alone restarts the operating system; a checkpoint resumes the captured machine.