Using virt-install to create unattended virtual machine install
Introduction
Virt-manager is a great tool to replace VirtualBox or VMware Workstation on Linux, when GNOME boxes is not enough.
I use virtualization on a daily basis and often need to set-up new virtual machines (VM). Virt-manager has a wizard to create new VM, where you specify the ISO, select all you need, but then we have to go through the installation process.
All that takes time, so to speed up, instead of going through the GUI, we'll use virt-install
(install virt-install
package if needed) to create an unattended installation of a debian server with just a serial console.
Unattended VM creation
We first need to create a password file for root
and one for the user. They are in plaintext, and the password must be on the first line:
echo "toor" > root-pwd.txt
echo "resu" > user-pwd.txt
Now let's do the installation:
virt-install --install debian12 \
--graphics none \
--console pty,target_type=serial \
--noautoconsole \
--extra-args="console=ttyS0,115200n8" \
--unattended profile=jeos,admin-password-file=root-pwd.txt,user-login=user,user-password-file=user-pwd.txt
Let's go through the options:
--install debian12
: Use debian 12. To list all the possible options, useosinfo-query os
. What we need is theshortcode
.--graphics none
: No graphics (We can add later on) but since this is a server, we'll just use SSH--console pty,target_type=serial
: Add a PTY console to the virtual machine--noautoconsole
: Don't automatically try to connect to the guest console--extra-args="console=ttyS0,115200n8"
: Arguments when starting the kernel to output to the serial console--unattended profile=jeos,admin-password-file=root-pwd.txt,user-login=user,user-password-file=user-pwd.txt
: Make an unattended installation. If not specified, we'll have to install ourselves.profile=jeos
: Use the JeOS profile (instead of defaultdesktop
)admin-password-file=password.txt
: Use the password in thepassword.txt
file we created just before forroot
.user-login=user,user-password-file=user-pwd.txt
: Create a user nameduser
with the password provided in the file. By default OpenSSH doesn't allowroot
to connect, so if we don't have a user, we won't be able to connect via SSH.
It will output something similar to:
Using default --name debian12
Using debian12 default --memory 2048
Using debian12 default --disk size=20
Starting install...
Retrieving 'linux' | 7.6 MB 00:14 ...
Retrieving 'initrd.gz' | 39 MB 01:05 ...
Allocating 'virtinst-pm9z6mm7-linux' | 0 B 00:00 ...
Transferring 'virtinst-pm9z6mm7-linux' | 0 B 00:00 ...
Allocating 'virtinst-c_yhjtv8-initrd.gz' | 0 B 00:00 ...
Transferring 'virtinst-c_yhjtv8-initrd.gz' | 0 B 00:00 ...
Allocating 'debian12.qcow2' | 0 B 00:00 ...
Creating domain... | 0 B 00:00
Domain is still running. Installation may be in progress.
You can reconnect to the console to complete the installation process.
We haven't specified the name of the VM (--name
), the network (--network
) to use, the location for the disk or its size (--disk
), as well the RAM (--memory
).
It defaulted to debian12
for the name. For the RAM and disk size, it uses defaults from its database. As for the network, it will use the default
adapter, and it will store the disk in the default
storage pool, which is in /var/lib/libvirt/images
.
It starts by downloading the necessary files to start the installation, then runs it in the background as requested.
Monitoring installation progress
While not necessary, we can look at the console to see the progress:
sudo virsh console debian12
We can also continue with virt-manager
instead.
It should complete in a few minutes, then it will poweroff the system.
It's a basic server with SSH, so we'll still need to adjust a few things such as setting the timezone, adding users, and providing SSH key for authentication.
Accessing the system via SSH
It's useful to have console access, but SSH is more practical.
List running domains
We can list the running virtual machines (aka domains) with sudo virsh list
:
user@fedora:~$ sudo virsh list
Id Name State
-------------------------
1 dev running
2 freebsd running
3 win11 running
4 spotify running
debian12
is not present in the list, so it must be in another state. We can add the --all
parameter to the command to list all the VM in any state:
user@ubuntu:~$ sudo virsh list --all
Id Name State
------------------------------------
1 dev running
2 freebsd running
3 win11 running
4 spotify running
- AI shut off
- debian12 shut off
- Kali shut off
- storage shut off
It's shut off at this time.
Start the domain
user@ubuntu:~$ sudo virsh start debian12
Domain 'debian12' started
Let's give it a few seconds to boot so that it can get an IP address.
Obtaining VM IP address
Our VM got attached to the default
network. We'll query the DHCP leases to figure out what IP address it got:
Expiry Time MAC address Protocol IP address Hostname Client ID or DUID
------------------------------------------------------------------------------------------------------------------------------------------------
2024-05-25 12:06:08 52:54:00:de:18:bb ipv4 192.168.100.168/24 - 01:52:54:00:12:18:aa
2024-05-25 12:25:07 52:54:00:de:98:bb ipv4 192.168.100.169/24 debian12 ff:00:12:18:aa:00:01:00:01:2d:e4:67:8b:52:54:00:12:18:aa
2024-05-25 12:19:29 52:54:00:a8:63:74 ipv4 192.168.100.181/24 pc 01:52:54:00:a8:68:75
2024-05-25 12:16:25 52:54:00:c4:28:fe ipv4 192.168.100.16/24 - 01:52:54:00:b5:28:ce
2024-05-25 12:16:56 52:54:00:32:05:09 ipv4 192.168.100.189/24 - 01:52:54:00:c2:83:63
We can easily identify our debian12
, it got 192.168.100.169. Now we're ready to SSH into it.
A few things
The big gotcha is that not all distributions are supported. We can list the ones that support the --unattended
option using the following script (on Ubuntu, we need the osinfo-db-tools
package):
#!/bin/bash
echo "Checking install scripts for each OS..."
# List all OSes and their short IDs
os_list=$(osinfo-query os)
# Loop through each OS and check if it has an install script
while IFS= read -r line; do
# Extract the short ID and the full name of the OS
short_id=$(echo "$line" | awk '{print $1}')
name=$(echo "$line" | awk -F '|' '{print $2}')
# Check if the OS has an install script
if osinfo-install-script "$short_id" &> /dev/null; then
echo "$name ($short_id) has an install script."
fi
done <<< "$os_list"
The database (containing the unattended installation scripts, URL locations, etc) gets updated once in a while, but we can also update it manually. Using the nightly
option to get the most recent development version (--latest
is a lesser adventurous option), it looks like this:
osinfo-db-import --nightly
A bit more than a month after the release of Ubuntu 24.04 and Fedora 40, only Fedora 40 support was added.
Furthermore, while a number of distributions have unattended installation, they don't always have an install tree URL. As an example, for Ubuntu, we have to go back all the way to 20.04 for the install tree URL to be in the database. Anything more recent than 20.04 doesn't have one available. However, if we have an install tree and provide its path (--location
), it should be able to do the unattended installation.