This repository provides a complete workflow for automated installation of Debian 12/13 and Ubuntu 22.04/24.04 using cloud-init. It supports both local KVM/libvirt environments and remote Proxmox servers, making it ideal for homelabs, DevOps automation, and reproducible VM provisioning.
It explains which cloud images work reliably, which ones should be avoided, and how to configure users, SSH keys, and password hashes. The project also demonstrates how to generate custom disk images from templates for fast and consistent VM deployment.
A key component is the Python-based TUI, which guides you step-by-step through:
- Selecting the distribution
- Choosing the correct cloud image
- Adding SSH keys and password hashes
- Generating cloud-init configuration
- Creating QCOW2 disk images
- Deploying to KVM/libvirt or Proxmox
This makes cloud-init–based VM provisioning accessible even for users without prior experience.
Do not use genericcloud images, they do not contain sata ahci drivers for cloud init.
cd /isos
# Debian
wget https://cdimage.debian.org/cdimage/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
wget https://cdimage.debian.org/cdimage/cloud/trixie/latest/debian-13-generic-amd64.qcow2
# Ubuntu
wget https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
wget https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img
The generator can also download missing base images automatically on first run.
This python script manages the full VM lifecycle:
- First run: asks for distro, architecture, VM name, username, password, SSH key and network type
- Saves parameters to a
.sessionfile for subsequent runs - Subsequent runs: detects existing VM, offers to show IP or recreate it
- Automatically generates
cloud-init.yml,meta-data.ymland copies them to/isos - For Ubuntu: additionally creates a seed ISO (
genisoimage) and anetwork-config.yml - Creates the overlay disk image and runs
virt-install
It needs mkpasswd (packaged with whois) and genisoimage to work.
sudo apt-get install whois genisoimage# build
uv build
# install globally
uv tool install dist/debian_cloud_init-0.1.0-py3-none-any.whl
# run from any directory containing a templates/ folder
debian-cloud-init
# uninstall
uv tool uninstall debian-cloud-init
# reinstall after rebuild
uv tool install --force dist/debian_cloud_init-0.1.0-py3-none-any.whluv run debian-cloud-init
# or
uv run python -m debian_cloud_init.generatorFragt alle Parameter interaktiv ab und gibt am Ende den fertigen Einzeiler-Befehl aus — inklusive gehashtem Passwort. Den Befehl kannst du dann direkt kopieren und ausführen.
uv run python -m debian_cloud_init.generator --onelineBeispiel-Ausgabe:
uv run python -m debian_cloud_init.generator --vmname=debian13 --username=wlanboy --distro=debian/13 --arch=amd64 --ssh-key=/home/user/.ssh/id_rsa.pub --hashed-password='$6$...' --net-type=default
Wenn alle Pflicht-Parameter als Flags übergeben werden, wird die Session übersprungen und die VM direkt erstellt:
uv run python -m debian_cloud_init.generator \
--vmname=debian13 \
--username=wlanboy \
--distro=debian/13 \
--arch=amd64 \
--ssh-key=~/.ssh/id_rsa.pub \
--hashed-password='$6$...' \
--net-type=default
# mit Bridge-Netzwerk:
uv run python -m debian_cloud_init.generator \
--vmname=debian13 \
--username=wlanboy \
--distro=debian/13 \
--arch=amd64 \
--ssh-key=~/.ssh/id_rsa.pub \
--hashed-password='$6$...' \
--net-type=bridge \
--bridge-interface=eth0| Parameter | Werte | Beschreibung |
|---|---|---|
--vmname |
beliebig | Name der VM |
--username |
beliebig | Benutzername in der VM |
--distro |
debian/13, debian/12, ubuntu/24.04, ubuntu/22.04 |
Distro und Version |
--arch |
amd64, arm64 |
Ziel-Architektur |
--ssh-key |
Pfad | Pfad zum öffentlichen SSH-Key (.pub) |
--hashed-password |
SHA-512-Hash | Hash via mkpasswd -m sha-512 |
--net-type |
default, bridge |
Netzwerktyp (NAT oder Bridge) |
--bridge-interface |
z.B. eth0 |
Bridge-Interface (nur bei --net-type=bridge) |
| Distro | Version | amd64 | arm64 |
|---|---|---|---|
| Debian | 12 | ✔ | ✔ |
| Debian | 13 | ✔ | ✔ |
| Ubuntu | 22.04 | ✔ | ✔ |
| Ubuntu | 24.04 | ✔ | ✔ |
The templates/ directory contains files that are merged into the generated cloud-init.yml:
cloud-init-template.yml– base template (users and runcmd are overwritten by the generator)package-config.txt– list of runcmd lines to execute (one per line)system-config.txt– shell script injected as a runcmd blockamd64-tools.sh– additional tooling script (downloaded automatically if missing)
The Proxmox backend runs all operations remotely via SSH — the cloud image is downloaded directly on the Proxmox host, and cloud-init snippets are uploaded via SCP. No large file transfers to your local machine.
On your local machine:
- SSH key-based access to the Proxmox host must be set up:
ssh-copy-id root@<proxmox-host>
mkpasswdmust be installed:sudo apt-get install whois
On the Proxmox host:
- The
localstorage must have the Snippets content type enabled: Datacenter → Storage → local → Edit → check Snippets qemu-guest-agentshould be installed in the VM template or via cloud-initpackagesso that IP detection works after boot.
uv run debian-cloud-init-proxmox
# or after installing as wheel:
debian-cloud-init-proxmoxThe first run asks for all parameters interactively and saves them to .proxmox-session:
| Parameter | Default | Description |
|---|---|---|
| Proxmox Host | — | IP or hostname of the Proxmox server |
| SSH User | root |
SSH user on Proxmox |
| Node Name | pve |
Proxmox node name |
| VM ID | — | Numeric VM ID (e.g. 100) |
| Storage Pool | local-lvm |
Storage pool for disk and cloud-init drive |
| Snippets Path | /var/lib/vz/snippets |
Path for cloud-init snippet files on Proxmox |
| Network Bridge | vmbr0 |
Bridge interface for the VM network |
| Distro / Arch | debian/13, amd64 |
Same options as KVM backend |
| VM Name, User, Password, SSH Key | — | Same as KVM backend |
Subsequent runs detect the existing VM and offer to show the IP or recreate it.
cloud-init.ymlis generated locally from thetemplates/directoryuser-dataandmeta-dataare uploaded via SCP to the snippets directory on Proxmox- The cloud image is downloaded directly on the Proxmox host (into
/var/lib/vz/template/iso/) if not already present qm create→qm importdisk→ disk attached asscsi0→ resized to 30 GB- Cloud-init drive (
ide2) added,--cicustompointed at the uploaded snippets - VM is started; IP is retrieved via
pvesh+ qemu-guest-agent
uv build
uv tool install dist/debian_cloud_init-0.1.0-py3-none-any.whl
debian-cloud-init-proxmox# import a cloud image manually
qm create 100 --name debian13 --memory 4096 --cores 2 --net0 virtio,bridge=vmbr0
qm importdisk 100 /var/lib/vz/template/iso/debian-13-generic-amd64.qcow2 local-lvm --format qcow2
qm set 100 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-100-disk-0
qm set 100 --ide2 local-lvm:cloudinit
qm set 100 --cicustom "user=local:snippets/myvm-user-data.yml,meta=local:snippets/myvm-meta-data.yml"
qm set 100 --boot order=scsi0
qm resize 100 scsi0 30G
qm start 100
# delete a vm
qm stop 100
qm destroy 100 --destroy-unreferenced-disks 1 --purge 1
# get IP via guest agent
pvesh get /nodes/pve/qemu/100/agent/network-get-interfaces --output-format jsonmkpasswd -m sha-512 # to generate hash for your user password
cp cloud-init.yml /isos/cloud-init.ymlvirt-install --os-variant list
virsh net-start default # check if default network is running# create overlay disk image
qemu-img create -f qcow2 -F qcow2 -o backing_file=/isos/debian-13-generic-amd64.qcow2 /isos/debian13.qcow2 30G
virt-install \
--name debian13 \
--arch x86_64 \
--machine q35 \
--cpu host-passthrough \
--memory 4096 \
--vcpus 2 \
--disk /isos/debian13.qcow2,device=disk,bus=virtio \
--os-variant debian13 \
--virt-type kvm \
--graphics none \
--console pty,target_type=serial \
--network network=default,model=virtio \
--cloud-init user-data=/isos/cloud-init.yml,meta-data=/isos/meta-data.yml \
--boot uefi \
--noautoconsole \
--importUbuntu requires a seed ISO instead of --cloud-init due to EFI + IDE CDROM incompatibility.
# create overlay disk image
qemu-img create -f qcow2 -F qcow2 -o backing_file=/isos/ubuntu-24.04-server-cloudimg-amd64.img /isos/ubuntu2404.qcow2 30G
# create seed ISO (user-data = cloud-init.yml, meta-data = meta-data.yml, network-config = network-config.yml)
genisoimage -output /isos/ubuntu2404-seed.iso -volid cidata -joliet -rock \
/isos/cloud-init.yml /isos/meta-data.yml /isos/network-config.yml
virt-install \
--name ubuntu2404 \
--arch x86_64 \
--machine q35 \
--cpu host-passthrough \
--memory 4096 \
--vcpus 2 \
--disk /isos/ubuntu2404.qcow2,device=disk,bus=virtio \
--disk /isos/ubuntu2404-seed.iso,device=cdrom,bus=scsi \
--os-variant ubuntu24.04 \
--virt-type kvm \
--graphics none \
--console pty,target_type=serial \
--network network=default,model=virtio \
--boot uefi \
--noautoconsole \
--importvirsh destroy debian13
virsh undefine debian13 --remove-all-storage --nvram
virsh destroy ubuntu2404
virsh undefine ubuntu2404 --remove-all-storage --nvramsudo journalctl -u cloud-init
sudo cat /var/log/cloud-init.logsudo cloud-init clean
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final
