Boot Cloud Images with Qemu Libvirt

Before I setup my proxmox sever, I am using this workflow. Slow and unproductive but it works. Every process here can be automated with proxmox and terraform, if you are interested check my previous post (kausap ko lang sarili ko dito).

Table of Contents

Prerequisite

  • libvirtd is running and can run VM.
  • cloud-utils is installed

Verify if you can run qemu-img, virsh and cloud-locald.

Pool

First create pool, skip if you have already created one, or using the default pool - /var/lib/libvirt/images.

1virsh pool-define-as --name pool --typre dir --target /srv/nvme/libvirt

Network

I want my VM to get IP from my router, for that we need to create a bridge network. If you are okay with NAT then skip this step. /etc/netplan/01-bridge.yaml

 1network:
 2  version: 2
 3  renderer: networkd
 4  ethernets:
 5    eth0:
 6      dhcp4: no
 7
 8  bridges:
 9    br0:
10      interfaces: [eth0]
11      addresses: [192.168.254.69/24]
12      gateway4: 192.168.254.254
13      nameservers:
14        addresses: [8.8.8.8]
1netplan apply

Backing Image

Download the image, for this example we will be using debian cloud image.

1cd /srv/nvme/libvirt
2wget https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2

Create the backing image.

1qemu-img create -f qcow2 -b debian-13-generic-amd64.qcow2 -F srvmnldebvm001.qcow2 50G
  • -f qcow2 → format of the new overlay disk
  • -b backing/base image
  • -F qcow2 → this will be the format of the backing image, and the storage of the vm.
  • 50G → virtual size

Cloud-init

In this section you can create a directory anywhere you want.

1mkdir ~~/Documents/deb-cloud-init
2cd /Documents/deb-cloud-init
3touch meta-data.yaml user-data.yaml network-config.yaml

Create a cloud-init file. user-data.yaml

 1users:
 2  - name: mcbtaguiad
 3    ssh_authorized_keys:
 4      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtf3e9lQR1uAypz4nrq2nDj0DvZZGONku5wO+M87wUVTistrY8REsWO2W1N
 5        # the ENTIRE key must be a single line
 6    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
 7    groups: sudo
 8    shell: /bin/bash
 9
10ssh_pwauth: false
11disable_root: true
12
13packages:
14  - qemu-guest-agent
15
16runcmd:
17  - systemctl enable qemu-guest-agent
18  - systemctl start qemu-guest-agent

meta-data.yaml

1instance-id: srvmnldebvm001
2local-hostname: srvmnldebvm001

network-config.yaml

 1network:
 2  version: 2
 3  ethernets:
 4    enp1s0:
 5      dhcp4: no
 6      addresses: [192.168.254.201/24]
 7      nameservers:
 8           addresses: [8.8.8.8]
 9      routes:
10      - to: 0.0.0.0/0
11        via: 192.168.254.254

Seed ISO

This will provide initial configuration data (user-data, meta-data) to the virtual machines.

1cloud-localds \
2  --network-config=network-config.yaml \
3  srvmnldebvm001-seed.iso \
4  user-data.yaml meta-data.yaml

Create the VM

 1virt-install \
 2    --name srvmnldebvm001 \
 3    --memory 1024 \
 4    --vcpus 2 \
 5    --import \
 6    --disk path=/srv/nvme/libvirt/srvmnldebvm001.qcow2,format=qcow2,bus=virtio \
 7    --os-variant debian13 \
 8    --network bridge=br0,model=virtio \
 9    --graphics vnc,listen=0.0.0.0 \
10    --noautoconsole \
11    --cloud-init user-data=user-data.yaml,meta-data=meta-data.yaml,network-config=network-config.yaml \
12    --cdrom srvmnldebvm001-seed.iso

Connect to the VM or use virt-manager.

1virsh list --all
2virsh console srvmnldebvm001
3ssh mcbtaguiad@192.168.254.201 # IP in network-config.yaml

Press Ctrl + ] to exit console.

Clean Up

1virsh destroy srvmnldebvm001
2virsh undefine srvmnldebvm001
3virsh pool-destroy pool
4virsh pool-delete pool