Skip to content

Repository files navigation

Creating Proxmox Templates with cloud-init

Collection of example cloud-init config files and shell scripts to download cloud-images and create Proxmox templates.

Blog post: Golden Images and Proxmox Templates with cloud-init

Cloud-init Files

Shell Scripts

  • build-template: Create a proxmox template.
    • For systems using ZFS storage, a script with local-zfs defaults is available: build-template (ZFS).
  • image-update: Check for an updated image and download the latest or missing image.
    • Works with alma, centos, debian, fedora and ubuntu cloud images.
    • Requires curl
    • Note: This script will change the file extension to *.img for visibility in the Proxmox GUI, qm disk import will automatically convert disk image.
  • clone-and-provision: Clone a VM template to a VM and optionally performs additional provisioning steps

Systemd Templates

Use

  • Create or copy vendor-data.yaml into your snippets storage location, e.g. /var/lib/vz/snippets/ (default directory for the script):

    wget -P /var/lib/vz/snippets/ https://raw.githubusercontent.com/scaleoutsean/proxmox-template-scripts/refs/heads/master/cloud-init/vendor-data.yaml
  • Copy the scripts into /usr/local/bin on your Proxmox node(s):

    • For Systems using LVM Storage (Default) - scripts

      wget -P /usr/local/bin/ https://raw.githubusercontent.com/scaleoutsean/proxmox-template-scripts/refs/heads/master/scripts/{build-template,image-update}
    • For Systems using ZFS Storage - scripts-zfs

      wget -P /usr/local/bin/ https://raw.githubusercontent.com/scaleoutsean/proxmox-template-scripts/refs/heads/master/scripts/{image-update}
      wget -P /usr/local/bin/ https://raw.githubusercontent.com/scaleoutsean/proxmox-template-scripts/refs/heads/master/scripts-zfs/{build-template}
    • For those who may want to add extra disks or inject user credentials:

      wget -P /usr/local/bin https://raw.githubusercontent.com/scaleoutsean/proxmox-template-scripts/refs/heads/master/code-examples/clone-and-provision
  • Change the scripts ownership and permissions:

    chown root:root /usr/local/bin/{build-template,image-update,clone-and-provision}
    chmod +x /usr/local/bin/{build-template,image-update,clone-and-provision}
  • Confirm /usr/local/bin is added to PATH:

    root@pve:~$ echo $PATH
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    
    # test using tab-completion
    image[TAB] -> image-update

Image Download / Update Script

# SSH into your Proxmox host
user@desktop:~$ ssh root@pve

# usage
root@pve:~$ image-update -d <DISTRO_NAME> -r <RELEASE_NAME> [ARGS]
Code Examples
image-update -d centos -r 9
image-update -d debian -r 13
image-update -d fedora -r 42
image-update -d ubuntu -r 24
Alternative Usage
# usage
image-update <DISTRO_NAME>-<RELEASE_NAME> [ARGS]
# example
image-update ubuntu-24 --remove
CLI Flags Description Default Required
--distro, -d Specify the distribution name, e.g. ubuntu. Yes
--release, -r Specify the release name, e.g. focal or 20. Yes
--backup, -b Backup the existing image before updating. Not compatible with --remove false No
--date Append the date to the image name, e.g. ubuntu*-$DATE.img false No
--remove Remove image and backups before updating. Not compatible with --backup false No
--storage, -s Specify the image storage path /var/lib/vz/template/iso No

Adding the --backup flag will backup existing images for the set distribution and release before downloading the latest image, using the naming scheme: IMAGE_NAME.backup.img.

Adding the --date flag will append the release date to name in YYYY-MM-DD or YYYY-MMM-DD format, e.g. ubuntu-24.04-server-cloudimg-amd64-YYYY-MM-DD.img. The format is not configurable as it is pulled from the distribution's release page.

Automatically Check for Updates Using Cron

The majority of images are updated every 1 to 2 months, so be considerate and don't check more than once a month for new cloud images. Note: Consider staggering cron jobs to different minute (0), hour (2), and day (10) across distributions and releases.

0 2 10 * * /usr/local/bin/image-update -d <distro_name> -r <release_name>

Example:

# create/edit crontab
crontab -e

# create a job
10 3 15 * * /usr/local/bin/image-update -d ubuntu -r focal

Automatically Check for Updates Using Systemd Timers

Note on Distro Release Frequency

Please consider the typical release frequency when creating timers, as there is little advantage to creating daily or weekly timers.

  • Centos: monthly, at the end of the month.
  • Debian: bi-weekly to monthly.
  • Fedora: single release, no reason to create a timer for any version.
  • Ubuntu: bi-weekly, typically during the 2nd and 4th week of a month.

The image-update script can also parse a single argument for distribution and release, image-update ubuntu-24, which is useful in creating systemd timers. You can still pass the --remove and --storage flags as well, however, --distro and --release are overridden when using this approach.

To use systemd timers, add the template files to the /etc/systemd/system directory and start at step 3. Or create a service and timer template, as follows:

  1. Create a service, image-update@.service, in the /etc/systemd/system directory. Add additional script flags to ExecStart= after %i.

    [Unit]
    After=network-online.target
    Description= Check for updated cloud image %i
    Documentation=https://github.com/trfore/proxmox-template-scripts
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/image-update %i --remove
  2. Create a timer, image-update@.timer, in the /etc/systemd/system directory. Note: Change the day and time this script runs on, OnCalendar=*-*-10 02:00:00. To prevent multiple timers from triggering simultaneously, RandomizedDelaySec=3h will randomly select a time within a 3-hour window and AccuracySec=1us will prevent systemd from grouping the timers.

    [Unit]
    Description=Check for updated cloud image %i
    Documentation=https://github.com/trfore/proxmox-template-scripts
    
    [Timer]
    OnCalendar=*-*-10 02:00:00
    AccuracySec=1us
    RandomizedDelaySec=3h
    Persistent=true
    
    [Install]
    WantedBy=timers.target
  3. Reload the systemd configuration

    systemctl daemon-reload
  4. Create a Timer, using the format image-update@<DISTRO>-<RELEASE>.timer

    # enable and start a timer
    systemctl enable image-update@ubuntu-24.timer --now

Additional commands:

# view all timers
systemctl list-timers --all

# view timer status
systemctl status image-update@ubuntu-24.timer

# disable a timer
systemctl disable image-update@ubuntu-24.timer

# view script update logs
systemctl status image-update@ubuntu-24.service

Proxmox Template Script

build-template: A wrapper script around qm to build Proxmox templates. This script assumes the use of cloud-init vendor data file located at local:snippets/vendor-data.yaml. Detailed usage available on the blog post: Golden Images and Proxmox Templates with cloud-init, and example file: cloud-init/vendor-data-minimal.yaml.

# usage
build-template -i <vm_id> -n <vm_name> --img <vm_image> [ARGS]

Example:

build-template -i 9000 -n ubuntu24 --img /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img
CLI Flags Description Default Required
--id, -i Specify the VM ID (1-999999999) 9000 Yes
--name, -n Specify the VM name template Yes
--image, --img Specify the VM image (path), ex: /PATH/TO/image.{img,iso,qcow2} Yes
--bios, -b Specify the VM bios, seabios or ovmf seabios No
--machine Specify the VM machine type, q35 or pc for i440 q35 No
--memory, -m Specify the VM memory 1024 No
--net-bridge Specify the VM network bridge vmbr0 No
--net-type Specify the VM network type virtio No
--net-vlan Specify the VM network vlan tag 1 No
--os Specify the VM OS l26 No
--resize Increase the VM boot disk size 1G No
--storage, -s Specify the VM storage local-lvm No
--scsihw Specify the VM storage controller virtio-scsi-pci No
--vendor-file Specify the cloud-init vendor file vendor-data.yaml No

Clone and Provision Example

code-examples/clone-and-provision: Example script to clone a template VM, add an extra disk, and attach cloud-init user-data for user credentials, SSH key injection, and first-boot filesystem/mount setup.

# create a SHA-512 password hash for cloud-init
openssl passwd -6

# clone template 9000 into db-vm01 (ID 9101) and add a 16G extra disk
clone-and-provision \
  --template-id 9000 --id 9101 --name db-vm01 \
  --full-clone false \
  --datastore local-lvm --size 16G \
  --mountpoint /home/jack --fstype xfs \
  --net-bridge vmbr0 \
  --net-vlan null \
  --ci-user jack --ssh-pubkey-file /root/.ssh/id_ed25519.pub \
  --ci-password-hash '$6$...'

Notes:

  • The script writes a per-VM cloud-init snippet to /var/lib/vz/snippets/<vm-name>-user-data.yaml.
  • Linked clone is the default (--full-clone false) to avoid copying the full OS disk.
  • --size expects G or T units (for example 16G, 1T).
  • The script uses --virtio1 for the extra disk and sets a serial so the guest can mount by /dev/disk/by-id/virtio-<serial>.
  • --net-bridge sets the bridge= value on net0 (default behavior is to inherit bridge from the template).
  • --net-vlan controls the VLAN tag on net0; default is null, which removes an inherited tag=<id> from the cloned template NIC.
  • For directory-backed storage like local, the script automatically creates the extra qcow2 volume first, then attaches it if <storage>:<size> syntax is rejected.
  • On older Proxmox releases without qm disk create, the script falls back to qemu-img create plus qm disk rescan --vmid <id> for local storage.
  • Start the VM after cloning with qm start <vmid> so cloud-init can apply the new user-data.

Credit

  • trfore - original author and maintainer of upstream repository

License

See LICENSE File

References

Blog Post:

Linux Cloud Images:

Proxmox:

About

PVE VM template scripts for rapid provisioning of linked VM clones on NetApp E-Series with NFS VM

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages