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:
- Seals the image for cloning (clears machine-id, removes SSH host keys, enables cloud-init)
- Exports the ZFS pool cleanly
- Converts via
qemu-imgto your target format - 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:
- Replace your ISO — use the kldload ISO instead of a stock Ubuntu/Debian ISO
- Replace your preseed/autoinstall — OpenZFS handles partitioning, ZFS setup, and ZFSBootMenu installation automatically
- Keep your provisioners — your shell scripts, Ansible playbooks, and post-processors work unchanged
- 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
- ZFS Without GRUB — understand why this matters
- Cloud & Packer — advanced Packer workflows
- ZFS Zero to Hero — learn ZFS from the ground up
- ZFS Boot Chain — deep dive into how ZFSBootMenu works
- Build Your Own — explore all profiles and distros