Install KVM and Build a Custom Ubuntu 22.04 Cloud Image

This post documents the complete process of installing KVM on the management server and building a clean, optimized, minimal Ubuntu 22.04 VM template. This template will serve as the base image for all orchestrated VMs in the lab environment.


1. Install KVM

Install KVM, libvirt, and required virtualization tools:

apt -y install qemu-system-x86 libvirt-daemon-system libvirt-daemon virtinst \
               bridge-utils libguestfs-tools libosinfo-bin ovmf qemu-guest-agent \
               virt-manager

Ensure VMs auto-start when libvirtd restarts:

nano /etc/default/libvirt-guests

Uncomment and set:

ON_BOOT=start

2. Create a New Ubuntu 22.04 VM

Using virt-manager, create a new VM:

  • Name: ubuntu22
  • Disk size: 25 GB recommended
  • ISO: Ubuntu Server 22.04
  • Kernel: Select Install HWE Kernel

Once installation completes, reboot to begin cleanup and optimization.


3. Remove snapd Completely

Snap is not required for VM workloads; remove it:

snap list
snap remove lxd
snap remove core20
snap remove snapd
apt purge --remove snapd
rm -rf /root/snap/

4. Disable Swap

systemctl list-units | grep swap
systemctl stop swap.target
systemctl disable swap.target
systemctl mask swap.target
swapoff -a

Edit /etc/fstab and comment out any swap entries.


5. Ensure Deterministic Network Interface Names

Force legacy NIC naming (eth0) for automated Netplan provisioning.

Edit:

nano /etc/default/grub

Modify:

GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"

Apply:

update-grub
reboot

After reboot:

rm -f /etc/cloud/cloud.cfg.d/90-installer-network.cfg

Create minimal DHCP config for eth0 only (placeholder file for orchestration later).

Disable cloud-init networking:

echo "network: {config: disabled}" > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

6. Configure File Descriptor and Process Limits

echo "* hard nofile 65536" >> /etc/security/limits.conf
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nproc 65536" >> /etc/security/limits.conf
echo "* soft nproc 65536" >> /etc/security/limits.conf

7. Limit Journal Size

sed -i "s/#SystemMaxFileSize.*/SystemMaxFileSize=512M/g" /etc/systemd/journald.conf

8. Fix resolv.conf to Use systemd-resolved

ln -fs /run/systemd/resolve/resolv.conf /etc/resolv.conf

9. Install Standard Required Packages

apt -y install net-tools rsyslog bc fio iperf3 gnupg2 software-properties-common lvm2 nfs-common jq

10. Clean Up SSH Login Messages

Edit PAM config:

nano /etc/pam.d/ssh

Comment out:

#session    optional     pam_motd.so  motd=/run/motd.dynamic
#session    optional     pam_motd.so noupdate
#session    optional     pam_mail.so standard noenv

Disable MOTD news:

echo "ENABLED=0" >> /etc/default/motd-news

11. Disable Unwanted Timer Services

List timers:

systemctl list-units | grep timer

Disable:

systemctl stop apt-daily-upgrade.timer apt-daily.timer fwupd-refresh.timer \
               motd-news.timer update-notifier-download.timer update-notifier-motd.timer

systemctl disable apt-daily-upgrade.timer apt-daily.timer fwupd-refresh.timer \
                  motd-news.timer update-notifier-download.timer update-notifier-motd.timer

systemctl mask apt-daily-upgrade.timer apt-daily.timer fwupd-refresh.timer \
               motd-news.timer update-notifier-download.timer update-notifier-motd.timer

12. Disable Unattended Upgrades

systemctl stop unattended-upgrades.service
systemctl disable unattended-upgrades.service
systemctl mask unattended-upgrades.service

13. Disable Ubuntu Advantage Services

systemctl stop ubuntu-advantage-tools
systemctl disable ubuntu-advantage-tools
systemctl mask ubuntu-advantage-tools

14. Disable AppArmor and UFW

These security layers are unnecessary within a trusted, firewalled lab network.

systemctl stop apparmor ufw
systemctl disable apparmor ufw
systemctl mask apparmor ufw

15. Prepare System for Export

Remove unused packages:

apt autoremove --purge -y

Generate SSH key:

ssh-keygen

Set root password:

passwd

Enable root login:

PermitRootLogin yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2

Optimize filesystem and compact:

e4defrag /
fstrim -av
dd if=/dev/zero of=/zero.fill bs=1M status=progress
rm -f /zero.fill
fstrim -av

Shutdown:

history -c
shutdown -h now

16. Export the qcow2 Base Image

Use virt-sparsify to compress and generate the reusable base image:

virt-sparsify --compress /var/lib/libvirt/images/ubuntu22.qcow2  /root/kvm-local/ubuntu22/base.qcow2

This final QCOW2 file becomes the VM template used by the automation/orchestration system.