qm-template
A Python CLI that downloads cloud images and creates Proxmox VE VM templates 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
axel,aria2c,wgetorcurl, whichever is available, with a configurable number of parallel connections - Complete
qm createcommand: the whole template is assembled into a singleqm create ... --template 1invocation instead of a chain ofqm setcalls - TOML configuration: read with
tomllibfrom the standard library - Standard library only: Python >= 3.11; external commands are limited to
the downloader and the Proxmox VE
qm/pvesmtools
Requirements
- Python >= 3.11
- uv to install and develop
- A Proxmox VE host, normally running as root
- Proxmox VE (
qm,pvesm) for thecreatecommand - One of
axel,aria2c,wgetorcurlfor thedownloadcommand
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. It is created with the built-in defaults on
first run; use --config PATH or QM_TEMPLATE_CONFIG to point elsewhere.
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. create.sshkeys lists
inline SSH public keys and create.sshkeys_files lists key files; the contents
of both are merged and deduplicated by key fingerprint for Cloud-Init, and at
least one key is required. To create the file manually instead:
install -d /etc/qm-template
cp qm-template.example.toml /etc/qm-template/config.toml
Command-line options override the configured defaults.
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 --dry-run alpine
# list distros and their configured defaults
qm-template distros
--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. 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 and VM ID selection
qm-template create
# filter images with a regular expression on the relative path
qm-template create debian-13
# non-interactive
qm-template create --vm-id 9000 --vm-name debian-13-template
# inspect the assembled command without running it
qm-template create --dry-run --vm-id 9000
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
Supported distros
| 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
├── qm-template.example.toml
├── src/qm_template/
│ ├── cli.py # argument parsing and entry point
│ ├── commands.py # download / create / distros commands
│ ├── config.py # TOML settings and first-run config seeding
│ ├── checksum.py # checksum parsing and verification
│ ├── config.default.toml # default configuration shipped in the wheel
│ ├── download.py # axel / aria2c / wget / curl wrappers
│ ├── http.py # HTTP helpers and directory listings
│ ├── log.py # logging setup
│ ├── pve.py # qm/pvesm integration
│ ├── shell.py # grouped command rendering and execution
│ └── 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.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qm_template-0.1.0.tar.gz | 93.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qm_template-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 120.1 kB
Release files / qm_template-0.1.0.tar.gz
| Download URL | qm_template-0.1.0.tar.gz |
|---|---|
| Size | 93.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d720db27e6febc6bbb6efd1b039572bb64cc310199603cec5b7e5fc54ba9f508
|
|
BLAKE2b-256 checksum How to use checksums |
528837636d2c73c60eb31a8010e91c38de95a66e7ad6c4a9259986a3b905cf19
|
| 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 14, 2026.
Transparency logRelease files / qm_template-0.1.0-py3-none-any.whl
| Download URL | qm_template-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d254c90a3e56efea3f198902eab338b0fed307da5c78df211b21d3687a5b83c2
|
|
BLAKE2b-256 checksum How to use checksums |
211d38e6d3d62e996674d882bc1eec0efad71b7ca3d528e409064fc747b9c098
|
| 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 14, 2026.
Transparency log