Ubuntu-CloudInit-Docs turns a cloud image into a Proxmox template with qm commands
A short guide for setting up a Ubuntu VM template in proxmox using CloudInit
At a glance
- What is it?
- This guide walks through building a reusable Ubuntu VM template on Proxmox with cloud-init, from downloading a cloud image and resizing it to attaching a cloudinit drive and writing a vendor.yaml. It suits a homelab or cluster operator who clones VMs for a living, and it is hard-wired to UEFI, one storage name and a specific snippet workflow.
- Who is it for?
- This guide is a good fit for a homelab or small cluster operator who clones Ubuntu VMs constantly and wants SSH keys, a user account and a guest agent baked into a template, because every step is a concrete qm command you can read and repeat. It is a poor fit if you do not already run Proxmox, since nothing here generalises to another hypervisor, and a poor fit if your storage is not named `local-zfs` or your VMs boot via BIOS, because both are baked into the assumptions.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 138 days ago.
- What is it written in?
- GitHub does not report a main language for this repository.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The whole guide is qm commands against one VMID
The structure is worth understanding before you read a single line of it, because the guide is really a script in prose form. A single VMID, 8001 in the walkthrough, is threaded through every command: the VM is created with `qm create`, the disk is imported into it, the cloudinit drive is attached, the config is applied, and finally the VM is converted with `qm template`. Every command that takes the identifier must be edited consistently, and the documentation says so directly, telling you to change the 8001 to whatever you like and to replace it in the subsequent commands. The alternative is the sample scripts, which package the same sequence into a shell file per release, and which the guide describes as scriptable, since the whole point of a template is that the work happens once and the clones are cheap. The repository is MIT licensed and its content is short: a readme, a licence file and a `samples/` directory holding `ubuntu/` and `debian/` subfolders.
Three assumptions narrow the guide before it starts
The guide states its assumptions up front, and reading them first saves a lot of confusion later. You want your VMs to boot via UEFI as opposed to BIOS, which is why the create command uses `--bios ovmf --machine q35` and adds an efidisk. Your Proxmox node's main storage is called `local-zfs`, which is why the import and attach commands both name that volume. And you have SSH keys stored in `~/.ssh/authorized_keys` of your regular user's home folder, which is what the `--sshkeys` option later points at. These are not examples, they are requirements, and the first two are hard-coded in the commands. The storage name in particular appears in three separate places, and the guide does mention in passing that if your disk storage is something else such as local-lvm you should replace it. The Debian sample folder suggests the same workflow exists for another distribution, though the visible readme text covers Ubuntu only.
Downloading the cloud image and resizing it to 32G is what gives clones room
The first mechanical step is to fetch an official Ubuntu cloud image and grow it, and the guide does this over SSH on the Proxmox node. For 24.04 the commands are:
wget -q https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
qemu-img resize noble-server-cloudimg-amd64.img 32GFor 26.04 the same two commands use the resolute paths. The explanation for why the resize happens before anything else is the part that matters. The image is a sparse file, so it starts out at around 2GB on disk even though you have told it it is 32GB, and the guide is blunt about the consequence: unless you resize the image now, any cloned VMs will not have any storage. The target size is a suggestion, and the documentation says you can change the 32G to 20, 80 or whatever you like. The order of operations is the lesson. Resize the image, then import it, and every clone inherits a disk that can actually grow.
Creating the VM with ovmf, q35 and a serial console
The create command defines a machine that will never boot, because it exists only to be turned into a template. It sets the OS type to l26, gives it 1024 megabytes of memory, enables the Qemu Guest Agent with `--agent 1`, and picks ovmf with q35 for the firmware and machine type:
sudo qm create 8001 --name "ubuntu-2604-cloudinit-template" --ostype l26 \
--memory 1024 \
--agent 1 \
--bios ovmf --machine q35 --efidisk0 local-zfs:0,pre-enrolled-keys=0 \
--cpu host --sockets 1 --cores 1 \
--vga serial0 --serial0 socket \
--net0 virtio,bridge=vmbr0The annotations explain each choice rather than leaving you to guess. The memory figure does not really matter since the template will not start, but cloned VMs need adjusting. The guest agent is useful for things like seeing the IP addresses of the interfaces. The CPU type is set to host, with one socket and one core, and the core count is again something to change in the clones. The display is serial0 rather than virtio, with the note that a serial GPU type does let you copy and paste. The network interface is virtio on the bridge `vmbr0`, and if you want a VLAN tag you add `,tag=##` immediately after the bridge name with no spaces.
Importing the disk and attaching the cloudinit pseudo-drive
Four commands turn the empty shell into something a template can be built from. The first imports the image you downloaded, the second attaches it as a virtio-SCSI disk with discard enabled, the third sets the boot order to that disk, and the fourth adds the cloudinit drive:
sudo qm importdisk 8001 resolute-server-cloudimg-amd64.img local-zfs
sudo qm set 8001 --scsihw virtio-scsi-pci --virtio0 local-zfs:vm-8001-disk-1,discard=on
sudo qm set 8001 --boot order=virtio0
sudo qm set 8001 --scsi1 local-zfs:cloudinitTwo of those lines carry caveats. The import names local-zfs as the target, and the guide says to replace it if your storage is something else such as local-lvm. The `,discard=on` flag is conditional on the underlying storage, and the guide's advice is to omit it if your disk storage is not on SSDs, with the parenthetical that it should be. The last command is the one that makes the rest of the guide work, since it is what gives the VM a place to read its cloud-init configuration from. The first step of the whole procedure, before any of this, is a GUI action: in the Proxmox web interface, go to Datacenter, then Storage, click local, click Edit, set the Content drop down to snippets, and confirm.
vendor.yaml runs once, and its reboot is the point
The cloud-init configuration is written as a snippet file in `/var/lib/vz/snippets`, and the guide creates it with a heredoc:
mkdir /var/lib/vz/snippets
cat << EOF | sudo tee /var/lib/vz/snippets/vendor.yaml
#cloud-config
runcmd:
- apt-get update
- apt-get install -y qemu-guest-agent
- systemctl enable ssh
- reboot
# Taken from https://forum.proxmox.com/threads/combining-custom-cloud-init-with-auto-generated.59008/page-3#post-428772
EOFThe file does two things and the guide separates them explicitly. The obvious one is installing the guest agent. The second is the reboot, and the reason is a cloud-init ordering problem: for some reason cloud-init starts after networking, so you cannot SSH or even ping the VM by the name you gave it. The package is only run once, and after this reboot the VM is usable by name. The guide also notes that the snippet content was taken from a forum thread on combining custom cloud-init with auto-generated configuration, and the comment is left in the file. Timezone and locale are appended to the same file with two echo commands that read the host's `/etc/timezone` and the `$LANG` variable, with the instruction to make sure your system's timezone and locale is correct first.
Pointing cloud-init at the snippet and setting the credentials
The configuration block is five `qm set` calls, and each one has a stated reason. The first tells cloud-init to use the vendor file, the second adds tags that show up in the Proxmox web interface and that clones inherit, the third names the user to create, the fourth sets the password, and the fifth imports SSH public keys so you can log in. The password line deserves attention because it passes the value through `openssl passwd -6 $CLEARTEXT_PASSWORD`, which means the cleartext password has to be sitting in an environment variable when you run it. The final line configures the network interface for DHCP on both address families, and the guide points out that this is supposed to be the default but that manual specifying is necessary. The `--sshkeys` option points at `~/.ssh/authorized_keys`, which is one of the three stated assumptions, so the keys have to be there before the template is built rather than after.
Converting to a template and the sample scripts that do it for you
The last action is a single command:
sudo qm template 8001After that the template is ready to clone, and the guide spells out the lifecycle: clone as you wish, adjusting cores and RAM as needed, then start the clone. On first boot cloud-init kicks in, creates your user, installs the guest agent and reboots, and once that reboot completes you can SSH in. The sample scripts exist to remove the manual work, and the table lists them. The Ubuntu 24.04 base template is `ubuntu-noble-cloudinit.sh` with a default VMID of 8200, the 24.04 variant with the NVIDIA Container Toolkit for the 590 server driver is `ubuntu-noble-cloudinit+nvidia+runtime.sh` with a default VMID of 8202, and the 26.04 base template is `ubuntu-resolute-cloudinit.sh` at 8200. At the top of each file are two environment variables, `VMID` and `STORAGE`, that you adjust to match your setup, which is why the quick start exports them before piping the script into bash. The full script for the base case is:
export VMID=8200 STORAGE=local-zfs
curl -fsSL https://raw.githubusercontent.com/UntouchedWagons/Ubuntu-CloudInit-Docs/main/samples/ubuntu/ubuntu-noble-cloudinit.sh | bashThe `curl` to `bash` pattern is worth a second thought in a security review, since the script is fetched over the network and executed immediately with your Proxmox privileges. Downloading the file and reading it first costs one command and is the difference between trusting a URL and trusting a specific revision of a script.
Editorial conclusion
This guide is a good fit for a homelab or small cluster operator who clones Ubuntu VMs constantly and wants SSH keys, a user account and a guest agent baked into a template, because every step is a concrete qm command you can read and repeat. It is a poor fit if you do not already run Proxmox, since nothing here generalises to another hypervisor, and a poor fit if your storage is not named `local-zfs` or your VMs boot via BIOS, because both are baked into the assumptions. Before following it, check that your Proxmox node has snippets enabled on the `local` dataset, which is a GUI step the guide puts first because everything after it depends on it, and note that the last push to the repository was on 2026-06-07, so the supported release list is the place to look when a new Ubuntu point release lands.
Frequently asked questions
What does the Ubuntu-CloudInit-Docs guide cover?
It is a short guide for setting up Ubuntu VM templates in Proxmox using cloud-init in a scriptable manner, and it states supported releases of Ubuntu 24.04 LTS (Noble Numbat) and Ubuntu 26.04 LTS (Resolute Raccoon). It assumes UEFI boot, a main storage called local-zfs and SSH keys in ~/.ssh/authorized_keys.
Why do I have to resize the Ubuntu cloud image before importing it?
The image is a sparse file, so it occupies around 2GB on disk even when the virtual size is larger, and the guide warns that unless you resize it now any cloned VMs will not have any storage. The walkthrough resizes to 32G with qemu-img, and says you can change the size to whatever suits.
Why does the vendor.yaml snippet reboot the VM?
The guide explains that cloud-init starts after networking, so you cannot SSH or even ping the VM by the name you gave it. The package is only run once, and after the reboot the VM can be reached by name.
Do I have to enable snippets before running the Proxmox commands?
Yes. The first step in the basics is a web interface change: under Datacenter, then Storage, click local, click Edit, set the Content drop down to snippets and confirm. The vendor.yaml file is written to /var/lib/vz/snippets, so without that setting the file is not visible to the hypervisor.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/untouchedwagons-ubuntu-cloudinit-docs)Was this useful?