TL;DR

Cloud qcow2 → qm createqm import-fromqm templateqm clone. Alma, Rocky, Ubuntu, etc.

You do not need an installer ISO if you just want Linux that boots with a user, an SSH key, and DHCP. Distros ship a GenericCloud / cloudimg (qcow2): import it, attach a cloud-init drive, turn it into a template.

Tested on Proxmox 8.0.4. The flow is this gist (fork of zidenis). Fedora 38 / Ubuntu 23.04 URLs go stale: point wget at the distro’s current cloud image.

1. Download the image

On the node, as root, on a storage with space (NFS, local, whatever you use):

1
2
3
4
5
6
export IMAGES_PATH="/mnt/pve/nfs-data/images/"
cd "${IMAGES_PATH}"

wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/CHECKSUM -O SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing

Other distros (uncomment one):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Amazon Linux 2 — https://cdn.amazonlinux.com/os-images/latest/
# wget https://cdn.amazonlinux.com/os-images/2.0.20230727.0/kvm/amzn2-kvm-2.0.20230727.0-x86_64.xfs.gpt.qcow2

# CentOS Stream 9
# wget https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2

# Fedora (bump the version)
# wget https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/Fedora-Cloud-Base-38-1.6.x86_64.qcow2

# Oracle Linux 9 — the .qcow is not qcow2; convert it
# wget https://yum.oracle.com/templates/OracleLinux/OL9/u2/x86_64/OL9U2_x86_64-kvm-b197.qcow
# qemu-img convert -O qcow2 -o compat=0.10 OL9U2_x86_64-kvm-b197.qcow OL9U2_x86_64-kvm-b197.qcow2

# RHEL 9 — needs a logged-in download from access.redhat.com

# Rocky 9
# wget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2

# Ubuntu cloudimg
# wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

2. VM and cloud-init variables

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export QEMU_CPU_MODEL="host"
export VM_CPU_SOCKETS=1
export VM_CPU_CORES=2
export VM_MEMORY=4098
export VM_RESOURCE_POOL="CustomResourcePool"

export CLOUD_INIT_USER="user"
export CLOUD_INIT_SSHKEY="/home/user/.ssh/id_rsa.pub"
export CLOUD_INIT_IP="dhcp"
export CLOUD_INIT_NAMESERVER="1.1.1.1"
export CLOUD_INIT_SEARCHDOMAIN="example.com"

export TEMPLATE_ID=1001
export VM_NAME="alma9"
export VM_DISK_IMAGE="${IMAGES_PATH}/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2"

--cpu host passes through the node CPU flags (faster; the template is not portable across CPU vendors). VM_MEMORY=4098 is the gist value; 4096 is fine.

The cloud-init user is not root. The key must be the .pub you will use from your laptop.

3. Create the base VM and import the disk

Swap local-lvm and vmbr0 for your real storage and bridge.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
qm create ${TEMPLATE_ID} --name ${VM_NAME} --cpu ${QEMU_CPU_MODEL} \
  --sockets ${VM_CPU_SOCKETS} --cores ${VM_CPU_CORES} --memory ${VM_MEMORY} \
  --numa 1 --net0 virtio,bridge=vmbr0 --ostype l26 --agent 1 \
  --pool ${VM_RESOURCE_POOL} --scsihw virtio-scsi-single

qm set ${TEMPLATE_ID} --scsi0 local-lvm:0,import-from=${VM_DISK_IMAGE}
qm set ${TEMPLATE_ID} --ide2 local-lvm:cloudinit --boot order=scsi0
qm set ${TEMPLATE_ID} --ipconfig0 ip=${CLOUD_INIT_IP} \
  --nameserver ${CLOUD_INIT_NAMESERVER} --searchdomain ${CLOUD_INIT_SEARCHDOMAIN}
qm set ${TEMPLATE_ID} --ciupgrade 1 --ciuser ${CLOUD_INIT_USER} --sshkeys ${CLOUD_INIT_SSHKEY}
qm cloudinit update ${TEMPLATE_ID}

--agent 1 asks for qemu-guest-agent. Alma/Rocky/Fedora ship it or cloud-init installs it (ciupgrade). Amazon Linux 2 and some Ubuntu cloudimgs will not show the guest IP in the Proxmox GUI until you install the agent yourself.

4. Convert to a template

1
2
qm set ${TEMPLATE_ID} --name "${VM_NAME}-Template"
qm template ${TEMPLATE_ID}

Do not start that VM again: clone it.

5. Clone and boot

1
2
3
export VM_ID=$(pvesh get /cluster/nextid)
qm clone ${TEMPLATE_ID} ${VM_ID} --name ${VM_NAME}
qm start ${VM_ID}

SSH with the private key that matches the .pub you fed cloud-init:

1
ssh user@192.168.0.123 -i ~/.ssh/id_rsa

If Proxmox does not show an IP: serial console, ip neigh on the bridge, or the router’s DHCP leases.

Source: gist inetshell/f1d0206d5319c11062845901e4f3d06b.