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 none, --console pty,target_type=serial and --extra-args console=ttyS0,115200 options.

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

This starts the installation and connects the current shell to the serial port of the VM. Later, this can be achieved with the virsh console example-vm command.

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.