HOWTO guides and templates for Infrastructure as Code (IaC) VMs using FreeBSD cloud-init images and config files
Find a file
2026-01-17 02:28:44 +01:00
cloudinit-data/simple Initial commit. 2026-01-17 02:28:44 +01:00
.gitignore Initial commit. 2026-01-17 02:28:44 +01:00
README.md Initial commit. 2026-01-17 02:28:44 +01:00

FreeBSD 15 cloudinit VMs

Configs and details for how to set up and run FreeBSD 15 cloudinit VMs in QEMU on Apple Silicon machines such as MacBook Pro M1.

Install tools on host

On your Apple Silicon machine, install Homebrew if you haven't already.

https://brew.sh/

Then use Homebrew to install QEMU, cdrtools, wget and xz:

brew install qemu cdrtools wget xz

Create directories on host

Create the following directories inside your local clone of this repo.

mkdir -p efi/ qcow2/ user-data-iso/ vm/

At this point, you should have the following directory structure:

.
├── README.md
├── cloudinit-data
│   └── simple
│       ├── meta-data.yaml
│       └── user-data.yaml
├── efi
├── qcow2
├── user-data-iso
└── vm

Download FreeBSD and EFI files

Download FreeBSD 15 VM image and uncompress it:

cd qcow2/
wget https://download.freebsd.org/releases/VM-IMAGES/15.0-RELEASE/aarch64/Latest/FreeBSD-15.0-RELEASE-arm64-aarch64-BASIC-CLOUDINIT-zfs.qcow2.xz
unxz FreeBSD-15.0-RELEASE-arm64-aarch64-BASIC-CLOUDINIT-zfs.qcow2.xz
cd ..

Download and extract EFI files:

cd efi/
wget https://gist.github.com/niw/4f1f9bb572f40d406866f23b3127919b/raw/f546faea68f4149c06cca88fa67ace07a3758268/QEMU_EFI-cb438b9-edk2-stable202011-with-extra-resolutions.tar.gz
tar xf QEMU_EFI-cb438b9-edk2-stable202011-with-extra-resolutions.tar.gz
cd ..

We will be reusing these files for each of our VMs.

Prepare files for VM

Create cloudinit config ISO for first VM from config files using mkisofs from cdrtools:

mkisofs \
  -o user-data-iso/simple.iso \
  -volid cidata \
  -joliet -rock \
  -input-charset utf-8 \
  -quiet \
  -graft-points \
  user-data=cloudinit-data/simple/user-data.yaml \
  meta-data=cloudinit-data/simple/meta-data.yaml

Create directory for VM files:

mkdir -p vm/simple/

Create EFI files for the VM:

dd if=/dev/zero of=vm/simple/pflash0.img bs=1m count=64
dd if=/dev/zero of=vm/simple/pflash1.img bs=1m count=64
dd if=efi/QEMU_EFI.fd of=vm/simple/pflash0.img conv=notrunc
dd if=efi/QEMU_VARS.fd of=vm/simple/pflash1.img conv=notrunc

Copy FreeBSD 15 cloudinit image for the VM:

cp \
  qcow2/FreeBSD-15.0-RELEASE-arm64-aarch64-BASIC-CLOUDINIT-zfs.qcow2 \
  vm/simple/hdd0.qcow2

At this point, you should have the following directory structure:

.
├── README.md
├── cloudinit-data
│   └── simple
│       ├── meta-data.yaml
│       └── user-data.yaml
├── efi
│   ├── QEMU_EFI-cb438b9-edk2-stable202011-with-extra-resolutions.tar.gz
│   ├── QEMU_EFI.fd
│   └── QEMU_VARS.fd
├── qcow2
│   └── FreeBSD-15.0-RELEASE-arm64-aarch64-BASIC-CLOUDINIT-zfs.qcow2
├── user-data-iso
│   └── simple.iso
└── vm
    └── simple
        ├── hdd0.qcow2
        ├── pflash0.img
        └── pflash1.img

Boot the VM in QEMU:

qemu-system-aarch64 \
  -M virt \
  -accel hvf \
  -cpu host \
  -smp 4 \
  -m 4096 \
  -drive file=vm/simple/pflash0.img,format=raw,if=pflash,readonly=on \
  -drive file=vm/simple/pflash1.img,format=raw,if=pflash \
  -device virtio-gpu-pci \
  -display default,show-cursor=on \
  -device qemu-xhci \
  -device usb-kbd \
  -device usb-tablet \
  -device intel-hda \
  -device hda-duplex \
  -drive file=vm/simple/hdd0.qcow2,format=qcow2,cache=writethrough \
  -nographic \
  -serial mon:stdio \
  -netdev user,id=net0,hostfwd=tcp::2222-:22 \
  -device virtio-net-pci,netdev=net0 \
  -device virtio-scsi-pci,id=scsi0 \
  -drive file=user-data-iso/simple.iso,format=raw,readonly=on,if=none,id=cdrom0 \
  -device scsi-cd,drive=cdrom0 

A bunch of text will scroll by, and you will see FreeBSD updating itself to latest version if necessary, and FreeBSD installing the packages that we have in the cloud-init configs, and doing other cloud-init steps.

After a pretty short amount of time, you should see the login prompt:

FreeBSD/arm64 (simple-vm) (ttyu0)

login: 

We see here that hostname has been set according to our cloud-init configs.

Log in as user simple-user

Enter password hest123

Now you are logged in on the VM :D