| your Linux re-packer
kldload — your platform, your way, anywhere, free
Source

Build ZFS on Root from Scratch

From ISO to golden template in 15 minutes

This guide walks you through building a clean ZFS-on-root image with ZFSBootMenu, exporting it to any format, and templating it with Packer for automated deployment. No GRUB. No Secure Boot signing headaches. Your image, your boot chain.

If you're coming from the ZFS Without GRUB page, this is the practical “how to actually do it” companion.

What you'll end up with: A deployable image (qcow2, VMDK, VHD, OVA, or raw) containing your choice of distro on ZFS with ZFSBootMenu. Ready for Packer, ready for your hypervisor, ready for bare metal. No vendor lock-in.

Prerequisites

  • The kldload ISO (free, ~8 GB)
  • A VM with at least 4 GB RAM and 40 GB disk, or a USB stick and bare metal
  • 10 minutes of your time

No internet required after boot — all packages and mirrors are baked into the ISO.

1 Boot the ISO

Option A: KVM / libvirt

# Create a VM and boot from the ISO
virt-install --name zfs-template --ram 4096 --vcpus 4 \
  --disk size=40,format=qcow2,bus=virtio \
  --cdrom kldload-free-latest.iso \
  --os-variant centos-stream9 \
  --network network=default,model=virtio \
  --graphics vnc,listen=0.0.0.0 \
  --boot uefi

Option B: Proxmox

# Upload ISO to Proxmox storage, then create a VM
qm create 100 --name zfs-template --memory 4096 --cores 4 \
  --scsi0 local-lvm:40 --cdrom local:iso/kldload-free-latest.iso \
  --net0 virtio,bridge=vmbr0 --bios ovmf --efidisk0 local-lvm:1
qm start 100

Option C: USB + Bare Metal

# Burn to USB (replace /dev/sdX with your USB device)
dd if=kldload-free-latest.iso of=/dev/sdX bs=4M status=progress oflag=direct conv=fsync && sync

# Boot from USB, select "KLDload Live" at the GRUB menu

2 Install with Core Profile

Once the live environment loads, open the web UI at http://localhost:8080 (or the machine's IP from another browser).

Select Your Distro

Ubuntu 24.04, Debian 13, CentOS Stream 9, Fedora 41, Rocky 9, RHEL 9, or Arch Linux. All install with ZFS on root.

Select Core Profile

Core gives you ZFS on root + ZFSBootMenu + stock distro. Nothing else. No kldload tools, no web UI, no opinions. Clean base for templating.

Or install from the CLI:

# Set your install options
export DISTRO=ubuntu          # ubuntu | debian | centos | fedora | rocky | rhel | arch
export PROFILE=core           # core = ZFS only, no extras
export TARGET_DISK=/dev/vda   # your target disk
export HOSTNAME=zfs-template

# Run the installer
sudo kldload-install-target

# ~5 minutes later: ZFS on root with ZFSBootMenu, ready to go

What happens during install:

1. Disk is partitioned: EFI System Partition + ZFS pool
2. ZFS pool created with datasets for /, /home, /var, etc.
3. Your distro is bootstrapped into the ZFS datasets
4. ZFS DKMS module built against the target kernel
5. ZFSBootMenu EFI binary installed to the ESP
6. Boot environments configured — first snapshot taken automatically

3 Export the Image

After installation, export the disk to your preferred format. The web UI has an export button, or use the CLI:

# Export to qcow2 (KVM / OpenStack)
sudo kexport --format qcow2 --output /tmp/zfs-template.qcow2

# Export to VMDK (VMware / ESXi)
sudo kexport --format vmdk --output /tmp/zfs-template.vmdk

# Export to VHD (Hyper-V / Azure)
sudo kexport --format vhd --output /tmp/zfs-template.vhd

# Export to OVA (portable VM archive)
sudo kexport --format ova --output /tmp/zfs-template.ova

# Export to raw (bare metal / dd)
sudo kexport --format raw --output /tmp/zfs-template.raw

The export process:

  1. Seals the image for cloning (clears machine-id, removes SSH host keys, enables cloud-init)
  2. Exports the ZFS pool cleanly
  3. Converts via qemu-img to your target format
  4. Optional: SCP to a remote host

The result is a cloud-init-ready golden template. Clone it, boot it, cloud-init handles hostname, SSH keys, and networking.

4 Packer Template

Feed the exported image to Packer for automated, repeatable builds. Here's a minimal Packer template:

# packer/zfs-template.pkr.hcl

source "qemu" "zfs-base" {
  iso_url          = "kldload-free-latest.iso"
  iso_checksum     = "file:kldload-free-latest.iso.sha256"
  disk_size        = "40G"
  format           = "qcow2"
  accelerator      = "kvm"
  ssh_username     = "root"
  ssh_password     = "kldload"
  ssh_timeout      = "20m"
  shutdown_command  = "shutdown -P now"
  boot_wait        = "30s"
  boot_command     = [
    "<wait30>",
    "curl -sf http://localhost:8080/api/install ",
    "-d '{\"distro\":\"ubuntu\",\"profile\":\"core\",",
    "\"disk\":\"/dev/vda\",\"hostname\":\"template\"}'<enter>"
  ]
  headless         = true
  memory           = 4096
  cpus             = 4
}

build {
  sources = ["source.qemu.zfs-base"]

  # Add your customizations here
  provisioner "shell" {
    inline = [
      "apt-get update && apt-get install -y your-packages",
      "systemctl enable your-service",
    ]
  }

  post-processor "manifest" {
    output = "manifest.json"
  }
}
# Build it
packer init .
packer build zfs-template.pkr.hcl

# Output: output-zfs-base/zfs-base (qcow2 image)
# ZFS on root, ZFSBootMenu, cloud-init ready, your packages installed

For Existing Packer Users

If you already build images with Packer, the key changes are:

  1. Replace your ISO — use the kldload ISO instead of a stock Ubuntu/Debian ISO
  2. Replace your preseed/autoinstall — OpenZFS handles partitioning, ZFS setup, and ZFSBootMenu installation automatically
  3. Keep your provisioners — your shell scripts, Ansible playbooks, and post-processors work unchanged
  4. Delete your GRUB config — ZFSBootMenu replaces it. No update-grub, no GRUB customization, no signing

Everything downstream of the base image — your packages, your configs, your services — stays exactly the same. Only the boot chain changes.

5 Deploy

KVM / libvirt

# Import the qcow2 image as a new VM
virt-install --name prod-server-01 --ram 8192 --vcpus 4 \
  --import --disk path=zfs-template.qcow2,format=qcow2 \
  --os-variant ubuntu24.04 --network network=default \
  --boot uefi --noautoconsole

Proxmox

# Import disk to Proxmox storage
qm importdisk 200 zfs-template.qcow2 local-lvm
qm set 200 --scsi0 local-lvm:vm-200-disk-0
qm set 200 --boot order=scsi0
qm start 200

AWS / Azure / GCP

# AWS: Import as AMI
aws ec2 import-image --disk-containers \
  "Format=raw,UserBucket={S3Bucket=my-bucket,S3Key=zfs-template.raw}"

# Azure: Upload VHD to blob storage, create image
az image create --resource-group myRG --name zfs-template \
  --source https://mystorage.blob.core.windows.net/vhds/zfs-template.vhd \
  --os-type Linux

# GCP: Import raw image
gcloud compute images create zfs-template \
  --source-uri gs://my-bucket/zfs-template.raw

Verify ZFSBootMenu

After deploying, verify the boot chain is correct:

# Check ZFS pool is mounted
zpool status
# NAME    STATE     READ WRITE CKSUM
# rpool   ONLINE       0     0     0

# List boot environments
zfs list -t snapshot -r rpool/ROOT
# NAME                            USED  AVAIL  REFER  MOUNTPOINT
# rpool/ROOT/ubuntu@install       200M      -   2.1G  -

# Verify ZFSBootMenu is the bootloader (no GRUB)
efibootmgr -v | grep -i boot
# Boot0001* ZFSBootMenu  HD(1,...)/File(\EFI\zfsbootmenu\vmlinuz.EFI)

# Verify no GRUB
which grub-install 2>/dev/null || echo "No GRUB installed. Good."
# No GRUB installed. Good.

Customize Your Image

The core profile gives you a blank canvas. Here's how to build on it:

Add Packages

Use your distro's package manager normally. apt install, dnf install, pacman -S — ZFS on root is transparent to userland.

Create Boot Environments

zfs snapshot rpool/ROOT/ubuntu@before-upgrade — take a snapshot before any change. Roll back at boot via ZFSBootMenu if anything breaks.

Add Datasets

zfs create rpool/data/postgres — separate datasets for databases, containers, logs. Each with its own compression, quota, and snapshot schedule.

Enable Encryption

zfs create -o encryption=aes-256-gcm -o keyformat=passphrase rpool/secure — native ZFS encryption. Unlock at boot via ZFSBootMenu.

The point: You now have ZFS on root with a modern boot chain that you control. No GRUB. No vendor signing dependencies. Snapshot, rollback, clone, encrypt, replicate — everything ZFS offers, with a bootloader that was built for it.

Canonical can change whatever they want. Your infrastructure doesn't care.

Next Steps