Proxmox 9 ships with a nice in-GUI importer for qcow2 images, which makes building VM templates from cloud images cleaner than it used to be. This post walks through building a reusable Alpine Linux template from the official Alpine cloud-init qcow2, including how to customize it before converting to template.

What you need

  • Proxmox VE 9.x
  • An Alpine cloud-init qcow2 image (the generic_alpine-*-uefi-cloudinit-*.qcow2 from alpinelinux.org if you want UEFI, or the bios-cloudinit variant if you’d rather use SeaBIOS)
  • The image uploaded to a storage pool with “Import” content type enabled (Datacenter > Storage > edit > check “Import”)

This guide uses UEFI. If your image filename has bios in it, switch the BIOS choice to SeaBIOS and skip the EFI disk.

Step 1: Create the VM shell

In the Proxmox web UI, click Create VM and walk through the tabs:

General

  • VM ID: 9000 (convention is to keep templates in a high range so they don’t collide with regular VMs)
  • Name: alpine-template

OS

  • Select Do not use any media (we’re importing a disk, not installing from ISO)
  • Type: Linux, 6.x - 2.6 Kernel

System

  • BIOS: OVMF (UEFI)
  • Machine: q35
  • Add EFI Disk: checked, on your preferred storage
  • Uncheck Pre-Enroll keys (Microsoft secure boot keys will block Alpine)
  • SCSI Controller: VirtIO SCSI single
  • Qemu Agent: checked (you’ll install the package inside Alpine later)

Disks

  • Delete the default scsi0 disk by clicking the trash icon (we’re importing the qcow instead)

CPU

  • Type: x86-64-v2-AES (broader compatibility than host, which matters if you ever migrate clones to a different node)

Memory

  • 512 or 1024 MB is plenty for the template; clones can be bumped

Network

  • Bridge: your bridge (vmbr0 usually), Model: VirtIO

Finish, but don’t start it.

Step 2: Import the qcow as the boot disk

In the left tree, expand the storage that holds your qcow imports. You’ll see an Import view under it. Select your Alpine qcow, click Import, and in the dialog:

  • Target: VM 9000
  • Storage: where the disk should live
  • Bus/Device: SCSI 0
  • Check Discard and SSD emulation if on SSD

Hit Import. Proxmox converts and attaches the disk in one step.

Step 3: Add the cloud-init drive

VM 9000 > Hardware > Add > CloudInit Drive. Leave it as ide2 on your storage of choice.

Step 4: Serial console and boot order

Alpine cloud images expect a serial console. Without it, the Proxmox VNC console may sit blank during boot.

  • Hardware > Add > Serial Port > Serial Number 0
  • Options > Display > set to Serial terminal 0
  • Options > Boot Order > enable scsi0, drag to top, disable everything else

Step 5: Initial cloud-init config

Cloud-Init tab on the VM. Set:

  • User: your username
  • SSH public key: paste your pubkey
  • IP Config (net0): dhcp for now (you’ll override per-clone later)

Click Regenerate Image at the bottom. This is required any time you change cloud-init settings. Skipping it is the #1 reason cloud-init changes don’t apply.

Step 6: Boot and customize

The stock Alpine cloud image is minimal. You’ll likely want to install packages or tweak configs before baking it into a template — things like qemu-guest-agent, common utilities, or whatever your clones will need every time.

Normally the rule is “never boot a template before converting it” because cloud-init generates machine-id and SSH host keys that would be inherited by every clone. That’s true, but the cleanup at the end of this step wipes those identity bits so the template stays generic. Boot, customize, clean, poweroff.

Start the VM, open the console, log in. Do whatever you need — apk add your packages, edit configs, whatever makes sense to bake into the template.

One thing you’ll see: cloud-init status --long may show an error from package_update_upgrade_install trying to run apk upgrade before networking is up. This is non-fatal — cloud-init whines but the VM works fine. If you want to silence it, add a drop-in before the cleanup:

cat > /etc/cloud/cloud.cfg.d/99-no-upgrade.cfg <<EOF
package_update: false
package_upgrade: false
package_reboot_if_required: false
EOF

This disables the package_update_upgrade_install module so it won’t try (and fail) on every clone’s first boot. If you want package updates later, do them via your own provisioning after the box is up and networking is solid.

Clean up before converting

When you’re done customizing, run this:

cloud-init clean --logs --seed
rm -f /etc/machine-id /var/lib/dbus/machine-id
rm -f /etc/ssh/ssh_host_*
poweroff

What this does:

  • cloud-init clean --logs --seed removes the “I already ran” marker so cloud-init re-runs on each clone’s first boot
  • Removing machine-id and SSH host keys ensures clones generate their own (otherwise every clone would share the same machine identity and SSH host fingerprint, which is a real problem)

After poweroff, proceed to convert.

Step 7: Convert to template

Right-click VM 9000 > Convert to template. The icon changes, Start is grayed out. You can no longer boot it directly, only clone it.

Step 8: Clone and use it

Right-click the template > Clone:

  • VM ID: a new one, e.g. 101
  • Name: whatever this VM is
  • Mode: Full Clone (Linked Clone has caveats around storage type and template deletion)

On the new VM:

  • Cloud-Init tab: set the IP, DNS, anything specific to this VM, click Regenerate Image
  • Hardware tab: bump memory/cores as needed
  • Start it

First boot, cloud-init reads the drive, applies your settings, generates fresh machine-id and SSH host keys. Log in via SSH with the key you set in the template.

Editing an existing template

Templates can’t be booted directly, but you’ll sometimes need to update one — add a package, change a config, or just re-bake against a newer Alpine qcow. The Proxmox pattern is clone, customize, re-template:

  1. Clone the template to a throwaway VMID (e.g. 9999), Full Clone
  2. Start it, make your changes, run the cleanup, poweroff
  3. Right-click 9999 > Convert to template
  4. Delete or rename the old template

Same workflow as Step 6. Takes about five minutes.

Why this is the right shape

Templates aren’t precious. They’re disposable artifacts you rebuild whenever the base image updates or you find a bug. Keep your template-build steps as a script (literally the one above plus the Proxmox CLI commands), and rebuilding from a fresh Alpine qcow is a ten-minute job. The whole point is that clones are where the per-host config lives; the template stays generic.

A few habits that keep things sane:

  • Number your templates by version. When Alpine 3.21 drops, build it as VMID 9001, keep 9000 around until you’ve migrated. Don’t update in place
  • Don’t put per-VM config in the template. Set defaults if you want (SSH key, default user), but leave IPs, hostnames, and anything VM-specific to the clone’s Cloud-Init tab
  • If you find yourself running the same setup commands after every clone, push them into the template instead, or into a cloud-init user-data snippet you can reference from clones

Once this is in place, building a new Alpine VM is Clone > set IP > Start > SSH in. Which is the whole point of templates.