Proxmox Templates โ VM Provisioning Guide โ
Proxmox templates let you provision new VMs in seconds by cloning a pre-configured base image. Combined with cloud-init, each clone boots with a unique hostname, SSH key, and IP configuration automatically.
What is a cloud-init template? โ
A cloud-init template is a VM that has been:
- Installed with a minimal OS
- Cleaned of unique identifiers (SSH host keys, machine ID)
- Configured with cloud-init to accept per-clone configuration
- Converted to a Proxmox template (made non-bootable as a standalone VM)
When you clone it, cloud-init applies the hostname, SSH keys, and network config on first boot.
Creating a template (AlmaLinux 9 example) โ
Step 1 โ Download the cloud image โ
bash
# On your Proxmox host
cd /var/lib/vz/template/iso
wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2Step 2 โ Create the VM โ
bash
# Create a VM with ID 9000
qm create 9000 --name "almalinux-9-template" --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 --ostype l26 --agent enabled=1
# Import the cloud image as disk
qm importdisk 9000 AlmaLinux-9-GenericCloud-latest.x86_64.qcow2 local-lvm
# Attach disk as primary drive
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
# Add cloud-init drive
qm set 9000 --ide2 local-lvm:cloudinit
# Set boot order
qm set 9000 --boot c --bootdisk scsi0
# Add serial console (required for cloud images)
qm set 9000 --serial0 socket --vga serial0Step 3 โ Configure cloud-init defaults โ
bash
qm set 9000 --ciuser root --cipassword "$(openssl passwd -6 'changeme')" --sshkeys /root/.ssh/authorized_keys --ipconfig0 ip=dhcp --nameserver "8.8.8.8 1.1.1.1"Step 4 โ Convert to template โ
bash
qm template 9000The VM is now a template. It cannot be started directly โ only cloned.
Cloning and deploying a new server โ
bash
# Clone the template to VM ID 101 with a new disk
qm clone 9000 101 --name "web-server-01" --full --storage local-lvm
# Set VM-specific cloud-init values
qm set 101 --ipconfig0 ip=10.0.0.10/24,gw=10.0.0.1 --ciuser root --hostname web-server-01.yourdomain.com
# Resize disk (optional โ expand from template default)
qm resize 101 scsi0 +20G
# Start the VM
qm start 101The VM boots, cloud-init runs, and within 30โ60 seconds it's accessible via SSH at the configured IP.
Automating with a script โ
bash
#!/bin/bash
# Usage: ./deploy.sh <vmid> <hostname> <ip> <gateway>
VMID=$1
HOSTNAME=$2
IP=$3
GW=$4
TEMPLATE=9000
qm clone $TEMPLATE $VMID --name "$HOSTNAME" --full --storage local-lvm
qm set $VMID --ipconfig0 ip=$IP/24,gw=$GW --hostname $HOSTNAME
qm resize $VMID scsi0 +10G
qm start $VMID
echo "VM $VMID ($HOSTNAME) starting at $IP"Maintaining templates โ
Update your template periodically by:
- Cloning the template to a temporary VM
- Starting it and running OS updates (
dnf update -y) - Running cloud-init cleanup:
cloud-init clean --logs - Shutting down the VM
- Converting back to template:
qm template TMPVMID - Deleting the old template and renaming the new one
Snapshot before converting back
Take a snapshot of the updated VM before converting to template, in case the update introduces issues and you need to roll back. 
