8.0 KiB
Proxmox Cloud Image Manager
Download, customize, and create VM templates from cloud images for popular Linux distributions. Enables fast VM deployment with cloud-init — just like a real cloud provider.
Requirements
- Proxmox VE host (script auto-elevates with
sudoif needed) - Packages:
curl,qemu-utils,libguestfs-tools(optional, for qemu-guest-agent injection)
Usage
Usage: pve-cloudimg.sh [options] <command> [args]
Commands:
list [distro] List available distros, cached images, and VM templates
download <distro> [ver] Download image into cache only (no VM, no modifications).
Use "all" to download every supported distro.
create <distro> [ver] Download cloud image (if needed) and create VM template
update <distro> [ver] Re-download and re-create a template (remove + create)
remove <distro> [ver] Remove cached image and VM template for a distro
Options:
-d, --dir DIR Image cache directory (default: <script-dir>/cloud-images/; set \$PVE_CLOUDIMG_DIR to change default)
--storage STORAGE Proxmox storage for VM disks (default: main-storage)
--bridge BRIDGE Network bridge (default: vmbr0)
--vlan VLAN VLAN tag for the network interface
--bios BIOS Firmware: seabios (default) or ovmf (UEFI)
--vm-id VMID VM ID for the template (default: auto from 9000)
--cores CORES CPU cores (default: 2)
--memory MEMORY Memory in MB (default: 2048)
--disk-size SIZE Disk size in GB (default: 10)
--user USER Cloud-init default user (default: distro-specific)
--ssh-key FILE SSH public key file to inject via cloud-init
--no-agent Skip installing qemu-guest-agent
--packages PKGS Extra packages to install (comma/space-separated, passed to virt-customize)
-f, --force Re-download image even if already cached
-y, --yes Skip confirmation prompts
Examples
# List available distros, cached images, and templates
./pve-cloudimg.sh list
# Pre-cache an image (download only, no modifications)
./pve-cloudimg.sh download debian
# Pre-cache every supported distro
./pve-cloudimg.sh download all
# Create a template from the default distro version (Ubuntu 26.04)
./pve-cloudimg.sh create ubuntu
# Create from a specific version with custom settings
./pve-cloudimg.sh create ubuntu 24.04 --storage local-btrfs --disk-size 64 --ssh-key ~/.ssh/id_rsa.pub
# Create with custom user and resources
./pve-cloudimg.sh create debian --user admin --cores 4 --memory 8192 --bridge vmbr1
# Create on a specific VLAN
./pve-cloudimg.sh create ubuntu --vlan 100
# Install extra packages alongside the guest agent
./pve-cloudimg.sh create ubuntu --packages "htop nfs-common"
# Live dangerously — Arch Linux rolling release
./pve-cloudimg.sh create arch
# Force re-download even if cached (adds --force to any command)
./pve-cloudimg.sh download ubuntu --force
./pve-cloudimg.sh create ubuntu --force
# Update to the latest image (re-download and re-create template)
./pve-cloudimg.sh update ubuntu
# Replace an existing template with a completely different distro
./pve-cloudimg.sh remove ubuntu
./pve-cloudimg.sh create debian
# Remove a template and its cached image
./pve-cloudimg.sh remove ubuntu 24.04
Supported Distributions
| Distro | Default | Versions | Default User |
|---|---|---|---|
| Ubuntu | 26.04 | 26.04, 24.04 | ubuntu |
| Debian | 13 | 13, 12 | debian |
| Arch Linux | rolling | rolling | arch |
How It Works
- Download (download command) — Fetches the official cloud image from the distro's mirrors and caches it locally. No modifications.
- Resize (create command only) — Expands the disk to the requested size on a working copy (cloud-init's
growpartresizes the partition on first boot) - First-boot script (create command only) — Writes a systemd oneshot service via
virt-customizethat installs packages (qemu-guest-agent + any extras) on first boot of each cloned VM - Create VM (create command only) — Configures CPU, memory, VirtIO SCSI controller, serial console, and QEMU guest agent
- Cloud-Init (create command only) — Sets default user, optional SSH key, and DHCP networking
- Template (create command only) — Converts the VM to a Proxmox template ready for cloning
Use download to just fetch and cache the image without any modifications. Use create for the full pipeline (resize + firstboot installer + import + template).
Deploying a VM from a Template
# Clone the template — repeat for as many VMs as you need
qm clone <template-vmid> <new-vmid> --name my-vm --full
# Or with a specific target storage
qm clone <template-vmid> <new-vmid> --name my-vm --full --storage local-btrfs
Customizing Cloud-Init Per-VM
After cloning, edit the clone's cloud-init config:
qm set <new-vmid> --ciuser myuser --sshkeys ~/.ssh/id_rsa.pub
qm set <new-vmid> --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1
qm set <new-vmid> --nameserver 1.1.1.1
qm set <new-vmid> --searchdomain example.com
qm start <new-vmid>
QEMU Guest Agent
Rather than installing packages inside a chroot during image preparation (which breaks on many distros due to Landlock, missing dnf, incompatible arch detection, etc.), the create command writes a first-boot installer into the working copy via virt-customize --firstboot-command. This creates a systemd oneshot service that runs on the first boot of each cloned VM:
| Distro family | Runs on first boot |
|---|---|
| Ubuntu, Debian | apt-get update && apt-get install -y qemu-guest-agent |
| Arch Linux | pacman -S --noconfirm qemu-guest-agent |
The installer runs in the actual VM environment with full kernel support, network access, and proper repos — no chroot limitations.
The download command does not apply any first-boot scripts — it only fetches and caches the raw cloud image.
To skip agent installation: --no-agent
Installing Extra Packages
Two ways to add packages beyond qemu-guest-agent:
Per-run with --packages:
./pve-cloudimg.sh create ubuntu --packages "htop nfs-common"
Per-distro by adding entries right under each DISTROS definition in the script:
DISTROS["ubuntu:26.04"]="..."
DISTRO_PACKAGES["ubuntu:26.04"]="htop nfs-common"
Packages with different names across distros get their own DISTRO_PACKAGES entry.
Adding a New Distribution
Edit pve-cloudimg.sh and add entries near the top of the file. Each distro gets one or more DISTROS lines, with an optional DISTRO_PACKAGES line directly below:
# Image definition
DISTROS["mydistro:1.0"]="https://example.com/image.qcow2|filename.qcow2|default-user|qcow2"
# Extra packages (optional, right below its DISTROS entry)
DISTRO_PACKAGES["mydistro:1.0"]="my-package other-package"
The DISTROS format is: url|filename|default_user|image_format
Then add a default version so create mydistro (without a version) works:
DISTRO_DEFAULT_VERSION[mydistro]="1.0"
Notes
- Run on the Proxmox host — the script auto-elevates with
sudoif not run as root - Images are cached in
<script-dir>/cloud-images/by default — override with-dor$PVE_CLOUDIMG_DIR - If you add the script to PATH (e.g.
/usr/local/bin/), setPVE_CLOUDIMG_DIR=/var/cache/proxmox-cloudimgin your shell rc so it doesn't try to write into/usr/local/bin/cloud-images/ - The script auto-increments VM IDs starting at 9000
- Cloud images from official distro mirrors — always up to date
- Use
-f/--forceto re-download an image even if already cached (e.g., to refresh a stale image) - Resized disks use cloud-init's built-in
growpartfor automatic partition resizing on first boot - Templates, clones, and cached images are independent — removing a template or cached image does not affect running VMs cloned from it
- To replace a template with a different distro,
removethe old one thencreatethe new one — they get separate VM IDs