Skip to content

Cloud-Init Templates

One-click cloud-image templates. Pick a curated upstream image (Ubuntu, Debian, Rocky Linux, AlmaLinux, Fedora, Alpine), pick a target node + storage, and PegaProx downloads the qcow2 / raw, runs qm importdisk, sets the cloud-init drive, and converts it to a template. No shell, no wget /tmp/... dance.

Curated images

The library is shipped with a vetted list of upstream cloud-images (URL + SHA256 + recommended config). Library entries are refreshed on update; from 1.3 a weekly CI job checks that every catalog URL still answers.

  • Ubuntu 22.04 / 24.04 / 26.04 LTS
  • Debian 12 / 13
  • Rocky Linux 9 / 10
  • AlmaLinux 9 / 10
  • Fedora Cloud Base 44
  • Alpine 3.24

The Enterprise Linux 10 images need a newer CPU model; the catalog passes the model they need (x86-64-v3). The image ids fedora-40, debian-11 and alpine-319 were removed in 1.3; a custom template or script that names them needs a current id.

Custom Templates

Paste your own http / https image URL. The same import flow runs - you get a template at the end. Useful for golden images, in-house base OS builds, or air-gapped registries (host the image on an http:// endpoint behind your own auth). From 1.3, a login written into the URL (https://user:password@host/...) is used for the download but shown as **** in the catalog and the deployment log.

Audit

The "Recent Deployments" widget shows who deployed what, when. Failed deployments include the full Proxmox task log line that broke. Audit-log entry includes the image URL + checksum.

Security

This was the C-1 finding from the v0.9.9 security audit and is now hardened:

  • Custom image URLs are parsed via urllib.parse - only http / https schemes are accepted (the deploy runs wget on the node, so file://, ftp://, etc. are rejected).
  • The URL is additionally rejected if it contains shell-hostile characters (space, ; | & ` $, quotes, backslash, newline, tab).
  • node, storage and VM name are validated against a ^[A-Za-z0-9._-]+$ regex whitelist before dispatch.
  • Every shell-bound argument in the qm / wget commands is wrapped in shlex.quote().

API

Method Endpoint Description
GET /api/templates/catalog Curated CATALOG + user-defined custom templates
POST /api/templates/custom Add a custom template (perm: vm.create). Body: {name, image_url, cores?, memory?, disk_gb?, distro?, version?, default_user?, tags?}
DELETE /api/templates/custom/{tpl_id} Delete a custom template (perm: vm.create)
POST /api/clusters/{cluster_id}/templates/deploy Kick off async deploy (perm: vm.create). Body: {template_id, node, storage, vmid?, name?}
GET /api/clusters/{cluster_id}/templates/deployments Recent deployments for a cluster, newest first (perm: cluster.view)
GET /api/templates/deployments/{dep_id} Detailed deployment status incl. log tail (perm: cluster.view)
GET /api/clusters/{cluster_id}/templates/existing Existing template VMs on the cluster (perm: cluster.view)