Installation of a libvirt VM over a serial console

Sometimes the graphical interface of a VM is not easily accessible, so an installation over serial interface is a convenient way to bootstrap a VM. This setup user the virt-install command that simplifies the setup. It uses libvirt pools to organise the VM image, in case adapt the --disk option if this is not desired.

The import bits are the --graphics, --console, --serial and --extra-args options.

virt-install \
    --name example-vm \
    --virt-type kvm \
    --boot uefi,bootmenu.enable=on,bios.useserial=on \
    --ram 2048 \
    --vcpus 1 \
    --cpu host \
    --network network=default,model=virtio \
    --graphics none \
    --video virtio \
    --channel spicevmc \
    --console pty,target.type=virtio \
    --serial pty \
    --extra-args 'console=ttyS0,115200n8 --- console=ttyS0,115200n8' \
    --rng /dev/random \
    --controller type=scsi,model=virtio-scsi,driver.iommu=on \
    --disk pool=default,size=10,format=qcow2,bus=virtio,discard=unmap,cache=none \
    --os-type linux \
    --os-variant debian10 \
    --location /path/to/debian-netinst.iso

This starts the installation and connects the current shell to the serial port of the VM. On later connect to the VM with the virsh console example-vm command. The arguments after the triple dash in the --extra-args option are used when writing the boot loader options. This allows one to see the Linux boot messages on the console on subsequent boots.

For Ubuntu 18.04 use the installer from the archive instead:

…
    --os-type linux \
    --os-variant ubuntu18.04 \
    --location http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/

Once the installation is finished, you can edit the VM settings and enable the UEFI boot menu over the serial console:

…
<os>
    <bootmenu enable='yes' timeout='3000'/>
    <bios useserial='yes' rebootTimeout='0'/>
</os>
…

Also, you might want to update the boot order mechanism. Remove the legacy <boot …/> tag from the os section and add <boot order='N'/> to each device you want to boot from.

…
<disk type='volume' device='disk'>
    <driver name='qemu' type='qcow2' discard='unmap'/>
    <source pool='default' volume='example-vm.qcow2'/>
    <target dev='sda' bus='scsi'/>
    <boot order='1'/>
</disk>
…
<disk type='file' device='cdrom'>
    <driver name='qemu' type='raw'/>
    <target dev='sdb' bus='sata'/>
    <readonly/>
    <boot order='2'/>
</disk>
…

Finally, in order to see the Linux boot messages, add the serial options to the GRUB_CMDLINE_LINUX variable in /etc/default/grub:

GRUB_CMDLINE_LINUX="console=ttyS0,115200n8"

Call update-grub to update the boot loader. Alternatively, remove grub2 and use the systemd-boot.