If you’re rolling out multiple virtual machines, it makes sense to create a clean image that doesn’t contain sensitive information such as SSH host keys, MAC addresses, logs, temporary files, and machine IDs. As a KVM admin, this is the point where virt-sysprep will be your best friend.
The virt-sysprep
utility is used to prepare a virtual machine (VM) image for cloning by removing or resetting specific configurations and data. It is a tool provided by libguestfs
package. By using virt-sysprep, we ensure that the cloned VM instance do not retain conflicting data.
Some common operations performed by virt-sysprep
:
- Hostname: Reset the hostname.
- SSH-related: Remove
~/.ssh/
and/etc/ssh/*key*
. - Logs: Clear log files in
/var/log/
. - User Accounts: Remove user accounts except
root
. - Temporary Files: Remove
/tmp/*
and/var/tmp/*
. - Machine ID: Reset
/etc/machine-id
.
Mastering KVM Virtualization - The Ultimate eBook
From home labs to production clouds - master KVM Host management, automating KVM administration using Terraform, Vagrant, and cloud automation. This eBook will enable you to build scalable virtual infrastructure that works whether you're learning at home or deploying enterprise solutions. Get your full copy today
Before we can use virt-sysprep
, ensure it’s installed by checking the version of the software.
virt-sysprep --version
If the virt-sysprep
command is not available in your system, then you can get it by installing the libguestfs-tools
package.
- Debian-based OS systems
sudo apt update
sudo apt install libguestfs-tools
- RHEL-based OS systems
sudo dnf install libguestfs-tools guestfs-tools
- openSUSE
sudo zypper -n install guestfs-tools
- Arch-based Linux
sudo pacman -S libguestfs guestfs-tools
Clone VM and run virt-sysprep
Below is a simple example of cloning a guest operating system using virt-clone
commands.
# Set guest names
SOURCE_VM="my-old-vm"
CLONED_VM="my-new-vm"
# Clone a VM
sudo virt-clone \
--original $SOURCE_VM \
--name $CLONED_VM \
--auto-clone
When a VM is cloned, all data from the original guest is carried over to the new one. However, in many cases, we don’t want this and instead aim to create a fresh instance based on a template. In such scenarios, it’s often necessary to clean up the image by removing log files, resetting SSH host keys (to establish a new identity), and performing similar housekeeping tasks to ensure the new VM is truly unique.
This operation is done by running the following after the virt-sysprep
command.
sudo virt-sysprep \
--domain $CLONED_VM \
--operations machine-id,dhcp-client-state,bash-history,logfiles,customize,ssh-hostkeys \
--firstboot-command 'dpkg-reconfigure openssh-server && systemctl restart ssh'
The tool modifies the disk image offline, meaning the VM should not be running.
If you don’t specify --operations
, all available operations will be executed, which might not be desirable. For instance, you may want to preserve the authorized SSH keys and avoid them being wiped.
To get a full list of virt-sysprep
command operations, run the following command:
virt-sysprep --list-operations
Note that the customize
operation is required for the --firstboot-command
option to function correctly.
Run virt-sysprep on a VM Image
virt-sysprep
can also be used to modify an existing VM image directly. This could be an image from an old VM or a cloud image that you want to configure, upload files to, and adjust internal settings before booting.
virt-sysprep --help
1. Specify disk image file
Use -a
or --add
followed by the disk file to specify the image file.
-a <file>
2. Setting custom root password
To set a custom root user password, use --root-password <SELECTOR>
. In this example we are setting root user password to StrongPassw0rd
--root-password password:StrongPassw0rd
3. Setting hostname
Use --hostname <HOSTNAME>
to set the hostname.
--hostname dbserver.cloudspinx.com
4. Install software package into VM image
The command options for installing software packages is --install <PKG,PKG..>
. Let’s consider and example to install openssh-server, mariadb-server, apache2, and php packages.
--install openssh-server,mariadb-server,apache2,libapache2-mod-php,php-mysql
5. Run command at first guest boot
We can provide the commands that will be run on first boot using --firstboot-command <'CMD+ARGS'>
--firstboot-command 'apt update && apt upgrade -y'
6. Set the default timezone
The default timezone can be set by passing --timezone <TIMEZONE>
--timezone America/Los_Angeles
7. Upload local file to destination
It is possible to upload files from your local machine into the image with --upload <FILE:DEST>
Let’s begin by creating a Hello World Bash script.
echo 'echo "Hello, World!"' > helloworld.sh
chmod +x helloworld.sh
The example below will copy the php script created into /root/
on the VM image.
--upload ./helloworld.sh:/root
Files can also be moved instead of copy by using --move <SOURCE:DEST>
8. Inject SSH public key into the guest
SSH public key can be injected into the guest VM using --ssh-inject <USER[:SELECTOR]>
Generate ssh keys if you don’t have:
ssh-keygen -t rsa -b 4096
The default path location for the SSH public key is ~/.ssh/id_rsa.pub
. We can inject this file.
--ssh-inject root:file:/home/$USER/.ssh/id_rsa.pub
root
is the name of guest OS user account/home/$USER/.ssh/id_rsa.pub
is the absolute path to the ssh public key. If using root user account this will be/root/.ssh/id_rsa.pub
The final set of virt-sysprep
commands to run in our example is as follows:
sudo virt-sysprep \
-a noble-server-cloudimg-amd64.img \
--root-password password:StrongPassw0rd \
--hostname dbserver.cloudspinx.com \
--network \
--firstboot-command 'apt update && apt upgrade -y' \
--timezone America/Los_Angeles \
--upload ./helloworld.sh:/root \
--ssh-inject root:file:/home/$USER/.ssh/id_rsa.pub
Sample execution output:
[ 0.0] Examining the guest ...
[ 12.4] Performing "abrt-data" ...
[ 12.4] Performing "backup-files" ...
[ 12.6] Performing "bash-history" ...
[ 12.6] Performing "blkid-tab" ...
[ 12.6] Performing "crash-data" ...
[ 12.6] Performing "cron-spool" ...
[ 12.6] Performing "dhcp-client-state" ...
[ 12.6] Performing "dhcp-server-state" ...
[ 12.6] Performing "dovecot-data" ...
[ 12.6] Performing "ipa-client" ...
[ 12.6] Performing "kerberos-hostkeytab" ...
[ 12.6] Performing "logfiles" ...
[ 12.6] Performing "lvm-system-devices" ...
[ 12.6] Performing "machine-id" ...
[ 12.7] Performing "mail-spool" ...
[ 12.7] Performing "net-hostname" ...
[ 12.7] Performing "net-hwaddr" ...
[ 12.7] Performing "net-nmconn" ...
[ 12.7] Performing "pacct-log" ...
[ 12.7] Performing "package-manager-cache" ...
[ 12.7] Performing "pam-data" ...
[ 12.7] Performing "passwd-backups" ...
[ 12.7] Performing "puppet-data-log" ...
[ 12.7] Performing "rh-subscription-manager" ...
[ 12.7] Performing "rhn-systemid" ...
[ 12.7] Performing "rpm-db" ...
[ 12.7] Performing "samba-db-log" ...
[ 12.7] Performing "script" ...
[ 12.7] Performing "smolt-uuid" ...
[ 12.7] Performing "ssh-hostkeys" ...
[ 12.7] Performing "ssh-userdir" ...
[ 12.7] Performing "sssd-db-log" ...
[ 12.7] Performing "tmp-files" ...
[ 12.7] Performing "udev-persistent-net" ...
[ 12.7] Performing "utmp" ...
[ 12.8] Performing "yum-uuid" ...
[ 12.8] Performing "customize" ...
[ 12.8] Setting a random seed
virt-sysprep: warning: random seed could not be set for this type of guest
[ 12.8] Setting the machine ID in /etc/machine-id
[ 12.8] Setting the hostname: dbserver.cloudspinx.com
[ 13.5] Installing firstboot command: apt update && apt upgrade -y
[ 13.5] Setting the timezone: America/Los_Angeles
[ 13.5] Uploading: ./helloworld.php to /root
[ 13.5] SSH key inject: root
[ 14.0] Setting passwords
[ 14.5] SELinux relabelling
[ 14.6] Performing "lvm-uuids" ...
9. Disable SELinux (Valid for RHEL based systems)
The --edit <FILE:EXPR>
allows you to edit file using Perl expression. Here is an example that will disable SELinux.
--edit '/etc/selinux/config: s/^SELINUX=.*/SELINUX=disabled/'
Or putting it in permissive mode:
--edit '/etc/selinux/config: s/^SELINUX=.*/SELINUX=permissive/'
For more usage guide of the virt-sysprep
command, run:
virt-sysprep --help
The resulting image can be imported to KVM using virt-install
commands. Check relevant chapter in the ebook for a step-by-step guidance.