Proxmox Cloud-Init Templates: Build Once, Clone in Seconds
How to build a reusable Proxmox VE cloud-init VM template: import a cloud image, attach the cloud-init drive, and clone fully configured VMs.
The whole workflow, in four moves:
- Import a distro cloud image into a VM shell instead of installing from an ISO.
- Attach a cloud-init drive (
qm set <vmid> --ide2 <storage>:cloudinit) so Proxmox can inject users, keys, and networking at boot. - Seal it with
qm template <vmid>— the disk becomes a read-only base image. - Clone and configure per VM with
qm cloneplus a handful of--ci*options.
Once the template exists, going from nothing to a booted, SSH-reachable VM with the right hostname and static IP is one clone and one start. No installer, no answer file, no post-install checklist.
Most Proxmox homelabs get built the slow way: run the Debian installer in a VM, configure it, then right-click and clone it forever. That works until the tenth clone, when you discover every one of them is fighting over the same DHCP lease and sharing SSH host keys. Cloud-init exists to solve exactly this, and Proxmox VE has first-class support for it built into both the web UI and qm.
Why cloud images, not ISO installs
Distro vendors publish purpose-built cloud images — Debian’s genericcloud qcow2 files, Ubuntu’s cloud images, Rocky/Alma’s GenericCloud builds. They differ from an ISO install in ways that matter here:
- They are already minimal. No desktop, no installer cruft, small root filesystem meant to be grown at first boot.
- cloud-init is pre-installed and enabled. That is the whole point of the image.
- They are pre-cleaned. Machine ID, SSH host keys, and cloud-init state are stripped so first boot produces a genuinely unique instance.
That last point is the one people underestimate. A template made from a hand-installed VM carries a baked-in /etc/machine-id, and on a systemd-networkd guest the DHCP client identifier is derived from it — so every clone asks the DHCP server for the same lease and they trample each other. Cloud images sidestep this by construction.
If you are still deciding whether this workload even wants a VM, Proxmox LXC vs VM is the right stop first. Containers have their own template mechanism and none of this applies to them.
Building the template
Download the cloud image onto the Proxmox node, then create an empty VM shell. Reserve a high VMID range (9000+) for templates so they never collide with real workloads.
qm create 9000 \
--name debian12-cloud \
--memory 2048 --cores 2 \
--net0 virtio,bridge=vmbr0 \
--scsihw virtio-scsi-single \
--ostype l26 \
--agent enabled=1
Now attach the downloaded image as the boot disk. Recent Proxmox versions let you allocate and import in one step:
qm set 9000 --scsi0 local-zfs:0,import-from=/var/lib/vz/template/iso/debian-12-genericcloud-amd64.qcow2,discard=on,ssd=1
The older two-step form still works if your version does not accept import-from (the command is spelled qm disk import in current releases, with qm importdisk kept as an alias):
qm importdisk 9000 debian-12-genericcloud-amd64.qcow2 local-zfs
qm set 9000 --scsi0 local-zfs:vm-9000-disk-0,discard=on,ssd=1
discard=on with ssd=1 lets the guest issue TRIM so deleted blocks actually get released back to thin-provisioned storage. On ZFS-backed storage this is the difference between a pool that stays lean and one that only ever grows; the Proxmox ZFS storage guide covers the pool-side settings that make it effective.
Then the pieces that make it a cloud-init template:
qm set 9000 --ide2 local-zfs:cloudinit
qm set 9000 --boot order=scsi0
qm set 9000 --serial0 socket --vga serial0
qm disk resize 9000 scsi0 32G
Four things happening there, each worth understanding:
- The cloud-init drive is a small ISO Proxmox generates from the VM config and presents on a CD-ROM drive; it is rebuilt when the VM cold-starts, not when a running guest reboots itself. (The GUI’s Regenerate Image button forces it in between.) The guest’s cloud-init reads it at boot. Without this drive, none of the
--ci*options do anything. - Boot order must name the imported disk explicitly. A freshly created VM often has the CD-ROM first, and a cloud image with no installer will just sit there.
- The serial console matters more than it looks. Many cloud images expect a serial console and send boot output there rather than to a virtual VGA display, so without
serial0the Proxmox console can show you a blank screen while the VM boots perfectly fine. That is a common “my cloud image is broken” report, and it is not broken. Proxmox’s own docs recommend adding a serial console and using it as the display for cloud-init images, while noting that some other images have problems with that configuration — so it is a strong default, not a universal one. - Resizing before sealing means every clone inherits the larger disk. Cloud images ship with a root filesystem measured in single-digit gigabytes; cloud-init’s growpart logic expands the partition and filesystem on first boot to fill whatever you gave it.
Bake in what every VM needs
Anything you would install on all clones belongs in the image, not in a post-clone script. libguestfs-tools on the Proxmox node lets you modify the qcow2 file offline, before you ever import it:
virt-customize -a debian-12-genericcloud-amd64.qcow2 \
--install qemu-guest-agent \
--run-command 'systemctl enable qemu-guest-agent' \
--truncate /etc/machine-id
The QEMU guest agent is the one to get right. Without it inside the guest, the --agent enabled=1 flag you set on the VM does nothing useful: Proxmox cannot read the VM’s IP addresses, cannot request a filesystem-consistent freeze during backups, and a “shutdown” from the UI falls back to an ACPI event the guest may ignore. That last point directly affects backup quality — see Proxmox Backup Server setup for why a quiesced filesystem matters when the snapshot is taken.
Truncating /etc/machine-id (leaving the file present but empty) tells systemd to generate a fresh one on first boot. Stock cloud images generally handle this already, but it costs nothing to confirm, and it is mandatory if you ever build a template from a manually installed VM.
For anything more elaborate than a package list, use a cloud-init snippet instead. Enable the snippets content type on a storage, drop a YAML file in it, and point the VM at it:
qm set 9000 --cicustom "vendor=local:snippets/vendor-data.yaml"
That gives you the full cloud-init config language — arbitrary runcmd, file writes, package repos, unit installs — rather than the small set of options Proxmox surfaces natively. Keep those snippets in version control. They are the actual definition of your base system.
Sealing and cloning
qm template 9000
This is one-way. The VM’s disks become read-only base volumes (base-9000-disk-0) and the template can no longer be started. To update it later, clone it to a scratch VM, boot, patch, run cloud-init clean --logs, shut down, and seal that as a new template with a new VMID. Version the names — debian12-cloud-2026-08 — so you always know which template a given VM descended from.
Cloning is where the per-VM identity gets applied:
qm clone 9000 120 --name web01
qm set 120 \
--ciuser ops \
--sshkeys ~/.ssh/authorized_keys \
--ipconfig0 ip=10.0.30.20/24,gw=10.0.30.1 \
--nameserver 10.0.30.1 --searchdomain lan
qm start 120
Use --sshkeys and skip --cipassword. Proxmox’s documentation recommends key-based authentication precisely because setting a password means Proxmox has to store an encrypted copy of it inside the cloud-init data; SSH keys are the intended path. Set ip=dhcp in --ipconfig0 if you would rather let the network assign addresses — and if those clones need to land on specific VLANs, the tagging setup is in Proxmox network bridges and VLANs.
Linked clones versus full clones
When the source is a template, Proxmox creates a linked clone by default: the new VM’s disk is a thin layer referencing the template’s base volume, so cloning is nearly instant and consumes almost no space up front. Adding --full forces an independent full copy instead.
The tradeoffs:
- Linked clones need storage that supports it — ZFS, Ceph RBD, LVM-thin, or qcow2 on a directory. Thick LVM and plain iSCSI cannot do it, and Proxmox does not quietly substitute a full copy: the clone fails outright with
Linked clone feature is not supported for drive 'scsi0'. On that storage you have to ask for--fullexplicitly. - A template whose base volume still backs linked clones cannot be removed. On the storage people actually use for this — ZFS, Ceph RBD, qcow2 on a directory — the base volume is load-bearing and deletion is refused while clones reference it. Plan template lifecycle accordingly, or your “clean up old templates” task will fail confusingly.
grep -r base-9000 /etc/pve/nodes/*/qemu-servertells you which VMs are holding it. - Full clones are the right default for anything long-lived and important. They cost disk and clone time; they buy you a VM with no dependency on a template you might want to retire.
Storage location also constrains where you can clone. A template on node-local storage produces clones on that same node — Proxmox restricts cloning to a different target node to templates sitting on shared storage. In a cluster, putting templates on Ceph or NFS means any node can spin one up directly. Proxmox cluster setup and the Ceph storage guide cover getting that shared layer in place.
Gotchas worth knowing before you hit them
- Editing cloud-init settings after first boot is not a no-op — it is the opposite. Proxmox derives the NoCloud
instance-idfrom a hash of the user-data and network-data it generates, so changing--ciuser,--sshkeysor--ipconfig0produces a new instance-id. On the next cold start the guest concludes it is a brand-new instance and re-runs the once-per-instance modules, which can regenerate SSH host keys and re-run first-boot scripts; the old user and old keys are not removed either. Two practical consequences: a change made while the VM is running will not reach the guest until a full stop/start (an in-guestrebootdoes not rebuild the drive), and identity is best set at clone time, before first start. - A blank console is usually the serial console problem, not a failed boot. Check for
--serial0 socket --vga serial0first. - UEFI changes the recipe. If you switch the template to OVMF you also need an EFI disk (
--efidisk0) and an image that actually boots UEFI. The default SeaBIOS path is the low-friction one for stock cloud images. --cpu hostis fast but pins you down. It exposes the host CPU’s full feature set, which is what you want on a single node, and it can block live migration between nodes with different CPU generations. Pick deliberately per cluster.--citypematters for non-Linux guests. Proxmox defaults to the NoCloud datasource for Linux and ConfigDrive-style for Windows. If a guest ignores its cloud-init config entirely, datasource mismatch is a prime suspect.
If a clone comes up unreachable, common Proxmox problems and fixes has the wider triage path.
Next steps
- Building the node this all runs on: how to install Proxmox VE.
- Templates make cluster nodes interchangeable — Proxmox cluster setup is the natural follow-on.
- Cloned VMs still need a backup policy: Proxmox backup and restore strategy.
Related
How to Install Proxmox VE: Step-by-Step Install Guide
Install Proxmox VE from ISO to first boot: the installer walkthrough, choosing ext4 vs ZFS, switching to the no-subscription repo, and a first-boot checklist.
Proxmox VE Hardware Requirements: Minimum vs Recommended
Proxmox VE hardware requirements: real minimum vs recommended specs, why ECC matters less than on a NAS, VT-x/VT-d flags, and boot SSD endurance.
Proxmox Network Bridges and VLANs: vmbr0, Tags, and OVS
How Proxmox VE networking really works: Linux bridge vs Open vSwitch, VLAN-aware bridges, tagging guests, and applying changes without locking yourself out.