How To

Create Rocky Linux 10/9/8 Template and VM on Proxmox

A pre-configured operating system can be saved as a template for faster virtual machines creation as day 2 operation. In this tutorial, we will create a Rocky Linux 9 and Rocky Linux 8 template from cloud images of the said OS. This applies only to the server edition releases of Rocky Linux, as the desktop variant will require installation from a DVD ISO image, which is beyond the scope of this article.

Original content from computingforgeeks.com - post 161448

Follow the following steps to create a customized OS template from Rocky Linux cloud images.

1 – Download Rocky Linux Cloud Images

Begin by downloading the Rocky Linux QCOW2 cloud images:

Use wget to directly download the latest generic Qcow2 images from source:

  • Rocky Linux 10:
wget https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2
# For LVM
wget https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-LVM.latest.x86_64.qcow2
  • Rocky Linux 9:
wget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
  • Rocky Linux 8:
wget https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud.latest.x86_64.qcow2

This should done on Proxmox server, or a KVM host where libguestfs-tools can be installed.

2 – Define template variables

Login to your Proxmox host, and list available configured storage pools:

# pvesm status
Name             Type     Status           Total            Used       Available        %
local             dir     active      1884995968        10524288      1874471680    0.56%
local-zfs     zfspool     active      1875328912          857128      1874471784    0.05%

In this guide, we will be using local storage pool. Next list configured networks:

# brctl show
bridge name	bridge id		STP enabled	interfaces
vmbr0		8000.5ced8cedb886	no		eno1
vmbr1		8000.5ced8cedb889	no		eno4
  • Rocky Linux 9:
# Image path
IMAGE=./Rocky-9-GenericCloud.latest.x86_64.qcow2
# VM ID / Template ID
TID=800
# Template name
TNAME=Rocky-9-Template
# Disk size 
SIZE=20G
# Network bridge
BRIDGE=vmbr0
# RAM
RAM=2048
# CPU cores
CORES=1
# Storage pool
STORAGE=local
# Cloud Init user
CUSER=rocky
# Cloud Init user password
CPASS="Str0ngUserPassW0rd"
  • Rocky Linux 8:
# Image path
IMAGE=./Rocky-8-GenericCloud.latest.x86_64.qcow2
# VM ID / Template ID
TID=801
# Template name
TNAME=Rocky-8-Template
# Disk size 
SIZE=20G
# Network bridge
BRIDGE=vmbr0
# RAM
RAM=2048
# CPU cores
CORES=1
# Storage pool
STORAGE=local
# Cloud Init user
CUSER=rocky
# Cloud Init user password
CPASS="Str0ngUserPassW0rd"

NATed network creation guide: Creating Private Network Bridge on Proxmox VE with NAT

3 – Image customization using virt-customize

We will need the virt-customize command which is provided by libguestfs-tools package. Let’s install it on Proxmox server or on a KVM node if you have one.

sudo apt update && sudo apt install libguestfs-tools

If installed properly, you should be able to check version.

virt-customize --version

We’ll use virt-customize to install basic packages, such as Vim, and qemu-guest-agent into the base cloud image.

# virt-customize -a $IMAGE --install vim,unzip,bash-completion,wget,curl,qemu-guest-agent
[   0.0] Examining the guest ...
[   8.2] Setting a random seed
[   8.3] Setting the machine ID in /etc/machine-id
[   8.3] Installing packages: vim unzip bash-completion wget curl qemu-guest-agent
[ 163.2] Finishing off

Use the same command to enable qemu-guest-agent service to start at boot.

# virt-customize -a $IMAGE --run-command 'systemctl enable qemu-guest-agent'
[   0.0] Examining the guest ...
[   7.6] Setting a random seed
[   7.6] Running: systemctl enable qemu-guest-agent
[   8.2] Finishing off

Set the default timezone for the OS. Replace UTC with your timezone, e.g `America/New_York”

# virt-customize -a $IMAGE --timezone "UTC"
[   0.0] Examining the guest ...
[   7.5] Setting a random seed
[   7.6] Setting the timezone: UTC
[   8.0] Finishing off

For better security, we recommend disabling SSH password authentication. To enable it, run the commands below:

# virt-customize -a $IMAGE --run-command 'sed -i "s/.*PasswordAuthentication.*/PasswordAuthentication yes/g" /etc/ssh/sshd_config'
[   0.0] Examining the guest ...
[   7.4] Setting a random seed
[   7.5] Running: sed -i "s/.*PasswordAuthentication.*/PasswordAuthentication yes/g" /etc/ssh/sshd_config
[   8.1] Finishing off

It is recommended to disable root user ssh login and use standard user set for cloud init.

# virt-customize -a $IMAGE --run-command 'sed -i "s/.*PermitRootLogin.*/PermitRootLogin no/g"  /etc/ssh/sshd_config'
[   0.0] Examining the guest ...
[   7.2] Setting a random seed
[   7.3] Running: sed -i "s/.*PermitRootLogin.*/PermitRootLogin no/g"  /etc/ssh/sshd_config
[   7.7] Finishing off

Relabel SELinux, as it is enforced by default.

# virt-customize -a $IMAGE --selinux-relabel
[   0.0] Examining the guest ...
[   7.4] Setting a random seed
[   7.4] SELinux relabelling
[   7.9] Finishing off

To disable SELinux completely, run the commands below.

# virt-customize -a $IMAGE --run-command ' sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config'
[   0.0] Examining the guest ...
[   4.7] Setting a random seed
guest
[   4.8] Running:  sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config
[   4.9] Finishing off

4 – Create Rocky Linux OS template on Proxmox

Expand the image disk to the size defined in the SIZE variable.

# qemu-img resize $IMAGE $SIZE
Image resized.

Create a VM with set ID, RAM, CPU and Network bridge.

qm create $TID \
--name $TNAME \
--memory $RAM \
--cores $CORES  \
--net0 virtio,bridge=$BRIDGE \
--scsihw virtio-scsi-pci \
--cpu host

We created a VM without disk attached. Import the base image we customized into the actual VM storage disk.

# qm importdisk $TID $IMAGE $STORAGE
importing disk './Rocky-8-GenericCloud.latest.x86_64.qcow2' to VM 801 ...
Formatting '/var/lib/vz/images/801/vm-801-disk-0.raw', fmt=raw size=107374182400 preallocation=off
...
transferred 89.6 GiB of 100.0 GiB (89.59%)
transferred 90.6 GiB of 100.0 GiB (90.59%)
transferred 91.6 GiB of 100.0 GiB (91.62%)
transferred 92.6 GiB of 100.0 GiB (92.62%)
transferred 93.6 GiB of 100.0 GiB (93.62%)
transferred 94.6 GiB of 100.0 GiB (94.62%)
transferred 95.6 GiB of 100.0 GiB (95.65%)
transferred 96.6 GiB of 100.0 GiB (96.65%)
transferred 97.6 GiB of 100.0 GiB (97.65%)
transferred 98.6 GiB of 100.0 GiB (98.65%)
transferred 99.6 GiB of 100.0 GiB (99.65%)
transferred 100.0 GiB of 100.0 GiB (100.00%)
transferred 100.0 GiB of 100.0 GiB (100.00%)
unused0: successfully imported disk 'local:801/vm-801-disk-0.raw'

After disk is imported, attach it to the VM created.

  • Using local dir storage volume
# qm set $TID --scsihw virtio-scsi-pci --virtio0 $STORAGE:$TID/vm-$TID-disk-0.raw
update VM 801: -scsihw virtio-scsi-pci -virtio0 local:801/vm-801-disk-0.raw
  • Using LVM or ZFS Pool:
# qm set $TID --scsihw virtio-scsi-pci --virtio0 $STORAGE:vm-$TID-disk-0
update VM 800: -scsihw virtio-scsi-pci -virtio0 local-zfs:vm-800-disk-0

Enable QEMU guest agent for the instance.

# qm set $TID --agent 1
update VM 801: -agent 1

Change boot order to start with VirtIO block device.

# qm set $TID --boot c --bootdisk virtio0
update VM 801: -boot c -bootdisk virtio0

Attach cloud init image:

# qm set $TID --ide2 $STORAGE:cloudinit
update VM 801: -ide2 local:cloudinit
Formatting '/var/lib/vz/images/801/vm-801-cloudinit.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=metadata compression_type=zlib size=4194304 lazy_refcounts=off refcount_bits=16
ide2: successfully created disk 'local:801/vm-801-cloudinit.qcow2,media=cdrom'
Use of uninitialized value in split at /usr/share/perl5/PVE/QemuServer/Cloudinit.pm line 106.
generating cloud-init ISO

Create a custom SSH public key file and paste your SSH key into it. Alternatively, you can generate an SSH key pair using ssh-keygen:

vim sshpub.key

Inject SSH key for the specified default user. Path to SSH public key can be changed accordinly.

# qm set $TID --sshkey ./sshpub.key
update VM 700: -sshkeys ssh-rsaxxxyyyzzz

Set default IP assignment to DHCP, and update username and password.

# qm set $TID --ciuser=$CUSER --cipassword="$CPASS" --ipconfig0 ip=dhcp
update VM 700: -cipassword <hidden> -ciuser rocky -ipconfig0 ip=dhcp

Assign a name to the VM using the following commands.

# qm set $TID --name $TNAME
update VM 700: -name Rocky-8-Template

Finally, convert the virtual machine into a template.

qm template $TID

5 – Creating a Virtual Machine from the Template

List available templates

# qm list
      VMID NAME                 STATUS     MEM(MB)    BOOTDISK(GB) PID
       800 Rocky-9-Template     stopped    8192              10.00 0
       801 Rocky-8-Template     stopped    2048             100.00 0

Define all the variables required to create a VM from template.

TID=800                           # Template ID
VMID=$(pvesh get /cluster/nextid) #ID of the VM to be created. This is auto-assigned by Proxmox VE
VMNAME=AppServer                  # Name of the VM
RAM=4096                          # VM RAM size in MB
CORES=2                           # CPU cores for the VM

It is also possible to define custom network configurations such as static IP address, gateway and netmask.

BRIDGE=vmbr0           # Name of the bridge attached to the VM
IP=192.168.10.11/24    # IP address (If using static IP addressing)
GW=192.168.10.1        # Default gateway IP Address
DNS=8.8.8.8            # Default DNS server
SDOMAINS="example.com" # Search domains, separate with , for multiple.

There are two standard options for cloning a template into virtual machine:

  • Linked Clone – A VM created from this type uses less disk space but depends on the base VM template and cannot run without access to it.
  • Full Clone – A VM created from a full clone is a complete copy and operates independently from the original VM template, but it requires the same amount of disk space as the original.

An example on full cloning of the template:

qm clone $TID $VMID --full --name $VMNAME --format qcow2

Linked clone example:

qm clone $TID $VMID --name $VMNAME

Modify CPU and RAM allocations:

# qm set $VMID --vcpus $CORES --cores $CORES
update VM 100: -vcpus 1

# qm set $VMID --memory $RAM
update VM 100: -memory 4096

Finally, update changes for the IP, DNS and Search domain(s) if you need static IP address:

# qm set $VMID --ipconfig0 ip=$IP,gw=$GW
update VM 100: -ipconfig0 ip=192.168.10.11/29,gw=192.168.10.1

# qm set $VMID --searchdomain $SDOMAINS
update VM 100: -searchdomain example.net

# qm set $VMID --nameserver $DNS
update VM 100: -nameserver 8.8.8.8

You can also provide a custom ssh public key to override the default.

qm set $VMID --sshkey $SSHKEY

Make sure the virtual machine is set to start on boot

qm set $VMID --onboot 1

The virtual machine can be started from CLI or web console

qm start $VMID

References:

Keep reading

Install KVM and Virt-Manager on Arch Linux Virtualization Install KVM and Virt-Manager on Arch Linux Virsh Commands Cheatsheet for KVM Virtual Machine Management KVM Virsh Commands Cheatsheet for KVM Virtual Machine Management Install VirtIO Drivers on Windows Server 2025 / Windows 11 KVM Install VirtIO Drivers on Windows Server 2025 / Windows 11 ZFS RAID Levels on Proxmox VE: Stripe, Mirror, RAIDZ1/2/3 Proxmox ZFS RAID Levels on Proxmox VE: Stripe, Mirror, RAIDZ1/2/3 Build a 3-Node Hyperconverged Ceph Cluster on Proxmox VE Proxmox Build a 3-Node Hyperconverged Ceph Cluster on Proxmox VE How To Install VirtualBox 7.x on Linux Mint 22 Linux Mint How To Install VirtualBox 7.x on Linux Mint 22

4 thoughts on “Create Rocky Linux 10/9/8 Template and VM on Proxmox”

  1. Hi there, your setup for templates looks a little bit complicated. I usually create installation of desired OS, make all basic setttings, install basic set of SW and convert it into a template.

    Reply
  2. Hey,

    Just wanted to let you know that this is a great work, virt-customize saves a bunch of times in doing modifications to the image. Also, I had to change the CPU configuration to the clone and add “host” as CPU type to make it work, it was stuck in grub all the time. Another point is that I had to change the serial to the default one (probably this is just from my Proxmox configuration.

    Thanks again for this tutorial!

    Reply

Leave a Comment

Press ESC to close