Skip to main content

qm-template

GitHub Actions Workflow Status PyPI - Version Docs

A Python CLI that downloads cloud images, creates Proxmox VE VM templates and prepares VirtualBox artifacts, with Cloud-Init support.

Features

  • Multi-distro downloads: Debian, Ubuntu, Rocky Linux, AlmaLinux, Fedora, CentOS Stream, Alpine, openSUSE and Arch Linux
  • Checksum verification: SHA-256/SHA-512 fetched from each distro's official checksum files and saved next to the image (<image>.sha256/.sha512)
  • Pinned builds: dated builds are selected where the upstream offers them, and images mirror the upstream directory layout
  • Resumable downloads: uses the first available of axel, aria2c, wget or curl, with a configurable number of parallel connections, progress output by default (silence it with --quiet) and a from-scratch retry with the same downloader when a download fails or fails checksum verification
  • Retrying metadata fetches: checksum and directory listings survive transient 5xx/network errors with exponential backoff
  • Complete qm create command: the whole template is assembled into a single qm create ... --template 1 invocation instead of a chain of qm set calls
  • Automatic VM IDs: the next free ID is picked from qm list (or the Proxmox config directory), starting at vmid.start (default 9000)
  • Configurable CPU type: create.cpu/--cpu overrides the default cputype=host when migration across CPU generations matters
  • Local VM artifacts: prepare converts an image to VDI, VMDK, QCOW2, raw or VHDX with qemu-img and builds a NoCloud seed ISO (user-data, meta-data and a DHCP network-config) with genisoimage, both written next to the source image
  • Validated TOML configuration: settings and per-distro overrides are validated with pydantic-settings (unknown keys are rejected with a hint), optional [distro.<name>] overrides, and QM_TEMPLATE_* environment variables that take precedence over the file
  • Shell completion: argcomplete completes commands, options and per-distro parameter values
  • Discoverable distro parameters: qm-template distros lists every parameter with its default and accepted values; qm-template distros debian describes a single distro
  • Mirror-friendly: point any distro at an upstream or mirror through [distro.<name>] base_url, including the checksum file
  • Configuration template: qm-template config prints a commented starting-point configuration and --full adds the per-distro default tables; the file is never written automatically

Requirements

  • Python >= 3.11
  • uv to install and develop
  • A Proxmox VE host, normally running as root
  • Proxmox VE (qm, pvesm) for the create command
  • One of axel, aria2c, wget or curl for the download command
  • qemu-img and genisoimage for the prepare command

Installation

Install the CLI as a uv tool:

uv tool install qm-template

For development, sync the project and run it from the virtual environment:

uv sync --group dev
uv run qm-template --help

Configuration

The configuration file is optional and loaded from /etc/qm-template/config.toml when it exists; use --config PATH/-c or QM_TEMPLATE_CONFIG to point elsewhere. All commands work without a configuration file. Settings are validated with pydantic-settings; unknown keys and invalid values are rejected with the file and key path in the error. Every setting can also be overridden with a QM_TEMPLATE_* environment variable that uses __ for nesting, for example QM_TEMPLATE_DOWNLOAD__CONNECTIONS=4; environment variables take precedence over the file, and command-line options take precedence over both. Downloaded images are stored in /var/lib/qm-template unless paths.images_dir overrides it, mirroring the upstream layout:

<images_dir>/<distro>/<release>/[<tag>/]<filename>

download.preferred orders the downloaders and download.connections sets the number of parallel connections for axel and aria2c. Downloads show progress by default; set download.quiet = true or pass -q/--quiet to hide it. cloudinit.user/password configure the Cloud-Init user, and cloudinit.sshkeys/sshkeys_files list inline SSH public keys and key files whose contents are merged and deduplicated by fingerprint; at least one key is required. create.cpu sets the CPU type passed as cputype=... (default host), and vmid.start/vmid.step drive automatic VM ID selection (default 9000 and 1). Optional [distro.<name>] overrides are not written to the generated file:

[distro.debian]
release = "bookworm-backports"
arch = "arm64"
base_url = "https://mirror.example.org/debian-cloud"

base_url points a distro at an upstream or mirror that mirrors the expected directory layout; the checksum file is fetched from the same base. Since the upstream sites generally do not GPG-sign checksum files, a mirror serves both the image and its checksum; use a trusted mirror if authenticity matters.

Print a starting-point configuration to stdout and redirect it; the file is never written automatically:

install -d /etc/qm-template
qm-template config > /etc/qm-template/config.toml

# also append the per-distro default tables
qm-template config --full > /etc/qm-template/config.toml

Shell completion is provided by argcomplete; enable it once per shell:

eval "$(register-python-argcomplete qm-template)"   # bash; zsh needs bashcompinit

Usage

Download a cloud image

# default distro from the configuration file
qm-template download

# pick a distro, optionally override parameters
qm-template download debian
qm-template download ubuntu --release noble --variant minimal
qm-template download debian --release bookworm --tag 20260907-2594

# print the first available downloader's command without running it
qm-template download -n alpine

# hide the downloader progress output
qm-template download -q alpine

# list distros with the values each parameter accepts
qm-template distros

# print a commented starting-point configuration
qm-template config > config.toml

--dry-run resolves the image and pretty prints the command of the first available downloader, one argument group per line, without downloading anything. Builds are pinned where the upstream provides dated snapshots (Debian, Ubuntu server, Arch Linux, openSUSE Tumbleweed): the newest build is selected, and a newer build is downloaded alongside the old one instead of overwriting it. Interrupted downloads are resumed on the next run; partial files are stored as <image>.part. A failed download is retried from scratch with the same downloader and never switches to another one; a completed download that fails checksum verification is downloaded once more from scratch before the command fails. Checksum files and directory listings are retried with exponential backoff on transient 5xx and network errors. The checksum fetched from the upstream source is saved next to the image as <image>.sha256 or <image>.sha512, depending on the upstream algorithm.

Create a VM template

# interactive image selection; the VM ID is chosen automatically
qm-template create

# filter images with a regular expression on the relative path
qm-template create debian-13

# non-interactive with an explicit ID
qm-template create --vm-id 9000 --vm-name debian-13-template

# use a migration-friendly CPU type
qm-template create --cpu x86-64-v2-AES

# inspect the assembled command without running it
qm-template create --dry-run --vm-id 9000

When --vm-id is omitted, qm-template collects the IDs in use from qm list (falling back to /etc/pve/qemu-server/*.conf) and picks the first free ID at or after vmid.start (default 9000), advancing by vmid.step (default 1); the 9000+ range keeps templates away from regular VMs. If qm create fails, an existing but incomplete VM config is reported with the qm destroy command needed to clean it up.

The resulting command is a single qm create invocation, which --dry-run pretty prints as:

qm create 9000 \
    --name debian-13-template \
    --cpu cputype=host \
    --cores 1 \
    --balloon 1024 \
    --memory 1024 \
    --net0 model=virtio,firewall=1,bridge=vmbr0 \
    --scsihw virtio-scsi-single \
    --agent type=virtio,enabled=1 \
    --machine q35 \
    --ostype l26 \
    --serial0 socket \
    --vga serial0 \
    --scsi0 local-lvm:0,import-from=/path/to/image.qcow2 \
    --scsi1 local-lvm:cloudinit \
    --boot order=scsi0 \
    --ipconfig0 ip=dhcp \
    --ciupgrade 0 \
    --ciuser debian \
    --cipassword debian \
    --sshkeys ~/.ssh/id_ed25519.pub \
    --template 1

Prepare local VM artifacts

Most hypervisors cannot boot .qcow2 directly, so guest disks have to be converted. The hypervisor-agnostic prepare command selects a downloaded image, converts it to a guest disk with qemu-img and packs a NoCloud seed into a CIDATA-labelled ISO with genisoimage: user-data/meta-data built from the [cloudinit] settings plus a DHCP network-config (needed because Debian cloud images do not fall back to a generated network configuration). Both artifacts are written next to the source image and only differ in suffix:

# debian-13.qcow2 -> debian-13.vdi + debian-13.iso
qm-template prepare debian-13

# other hypervisors: vmdk (VMware/VirtualBox), raw or vhdx (Hyper-V)
qm-template prepare --format vmdk debian-13

# override the hostname recorded in the seed
qm-template prepare --vm-name debian-13-vbox debian-13

# preview every command without running or writing anything
qm-template prepare --dry-run debian-13

Supported formats are vdi (default), vmdk, qcow2, raw and vhdx; the extension follows the format (--format vdi writes <image>.vdi). Choosing qcow2 for a .qcow2 source is rejected because it would overwrite the source image. For VirtualBox, attach the .vdi as a SATA hard disk and the seed ISO as a CD-ROM. Use the generic/genericcloud image variants: Debian's nocloud variant does not run Cloud-Init. An existing guest disk is kept and only the seed ISO is rebuilt, since converting is expensive and the seed derives from the [cloudinit] settings; pass --force/-f to convert again.

Supported distros

qm-template distros is the authoritative list, including the accepted values of every parameter. Only Debian and Fedora accept --tag for a specific build; using it with any other distro is an error. Defaults:

Name Default release Default variant Notes
debian trixie genericcloud --release accepts -backports
ubuntu resolute server variant minimal also supported
rocky 10 GenericCloud variant GenericCloud-LVM
almalinux 10 GenericCloud variant GenericCloud-ext4
fedora 44 Generic variant UEFI-UKI
centos 10 GenericCloud CentOS Stream
alpine 3.24 generic BIOS firmware, Cloud-Init enabled
opensuse tumbleweed Minimal --release 15.6 for Leap
archlinux latest cloudimg variant basic also supported

Development

uv sync --group dev
just lint        # ruff check --fix + ruff format
just typecheck   # ty check src/
just test        # pytest
just docs-build  # mkdocs build

Project layout:

qm-template/
├── pyproject.toml
├── config.example.toml
├── src/qm_template/
│   ├── cli.py          # argument parsing, completion and entry point
│   ├── commands.py     # download / create / prepare / distros / config commands
│   ├── config.py       # pydantic-settings models and configuration loading
│   ├── checksum.py     # checksum parsing and verification
│   ├── cloudinit.py    # SSH key collection and seed user-data/meta-data
│   ├── config.default.toml  # default configuration shipped in the wheel
│   ├── download.py     # axel / aria2c / wget / curl wrappers
│   ├── http.py         # HTTP helpers, retries and directory listings
│   ├── images.py       # local image discovery
│   ├── log.py          # logging setup
│   ├── pve.py          # qm/pvesm integration and VM ID selection
│   ├── shell.py        # grouped command rendering and execution
│   ├── prepare.py      # qemu-img / genisoimage wrappers
│   └── distros/        # one module per distro family
└── tests/

Documentation

The published documentation site lives at https://ak1ra-lab.github.io/qm-template/.

References

Metadata

Release files for qm-template 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qm-template 0.3.2
File Size Uploaded
qm_template-0.3.2.tar.gz 128.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qm-template 0.3.2
File Interpreter ABI Platform
qm_template-0.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 163.4 kB

Release files / qm_template-0.3.2.tar.gz

Download URL qm_template-0.3.2.tar.gz
Size 128.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ba32d7a7ef957edce7888bb9a82b4b08e43a56296c2e23d010f43a439b325674
BLAKE2b-256 checksum
How to use checksums
da7ef8e7b003cf9a6faa51914484d811b515aecf7eddc77639785cfb2a65ff0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / qm_template-0.3.2-py3-none-any.whl

Download URL qm_template-0.3.2-py3-none-any.whl
Size 34.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
47e83fa49d2cbbb85269440d2fb64527ba79d21f577070da4d5906393e1e4c32
BLAKE2b-256 checksum
How to use checksums
1a1b3040d79020a762d6cd4b8f3d6fe2f6be17d6bb830054a9d349ca08fa562d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.2 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page