Photo by Gabriel Heinzer on Unsplash
Recently I embarked on a journey to set up Kubernetes Cluster on my home lab running Proxmox. From my experience designing K8s architectures on AWS EKS and Azure AKS, I knew I would need to create multiple VMs so instead of having to create them 1 by 1 and repeating the same steps I decided to leverage automation and Cloud‑Init enabled VM templates to streamline the process.
How Cloud‑Init and Proxmox Automation Help
Cloud‑Init is a powerful tool designed for early initialisation of cloud instances. By leveraging Cloud‑Init with Proxmox, you can automate the initial configuration of your VMs, installing necessary packages, setting up SSH keys, and even pre-configuring network settings. Once the VM template is created, deploying new VMs becomes a matter of minutes rather than hours, ensuring that each instance is consistently configured and ready to run immediately.
More Automation Queue in Bash — Scripting 😍
Below is the Bash script that automates the entire process, streamlining the configuration and setup of your VM template. Why Type When You Can Script, Eh? 😏
#!/bin/bash
# VM and storage identifiers
VMID=10002
STORAGE=local-lvm
# Define the VM name and Ubuntu image parameters
VM_NAME="ubuntu-2404-cloudinit-template"
STORAGE="local-lvm" # Adjust if using a different storage backend
IMAGE_URL="https://cloud-images.ubuntu.com/daily/server/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
IMAGE_PATH="/var/lib/vz/template/iso/$(basename ${IMAGE_URL})"
# Cloud‑Init credentials (consider stronger hashing for production)
CI_USER="ubuntu"
CI_PASSWORD="$(openssl passwd -6 ubuntu)"
# Create an SSH public key file for injecting into the VM
echo "ssh-rsa PASTE YOUR PUBLIC KEY HERE" > ssh.pub
# 1. Download the Ubuntu 24.04 Cloud Image if not already present
if [! -f "${IMAGE_PATH}"]; then
echo "Downloading Ubuntu 24.04 Cloud Image..."
wget -P /var/lib/vz/template/iso/ "${IMAGE_URL}"
else
echo "Image already exists at ${IMAGE_PATH}"
fi
set -x # Enable command echoing for debugging
# Destroy any existing VM with the same VMID to avoid conflicts
qm destroy $VMID 2>/dev/null || true
# Create a new VM with UEFI support and custom hardware configurations
echo "Creating VM ${VM_NAME} with VMID ${VMID}..."
qm create ${VMID} \
--name "${VM_NAME}" \
--ostype l26 \
--memory 2048 \
--cores 2 \
--agent 1 \
--bios ovmf --machine q35 --efidisk0 ${STORAGE}:0,pre-enrolled-keys=0 \
--cpu host --socket 1 --cores 2 \
--vga serial0 --serial0 socket \
--net0 virtio,bridge=vmbr0
# Import the downloaded disk image into Proxmox storage
echo "Importing disk image..."
qm importdisk ${VMID} "${IMAGE_PATH}" ${STORAGE}
# Attach the imported disk as a VirtIO disk using the SCSI controller
qm set $VMID --scsihw virtio-scsi-pci --virtio0 $STORAGE:vm-${VMID}-disk-1,discard=on
# Resize the disk by adding an additional 8G
qm resize ${VMID} virtio0 +8G
# Configure the VM to boot from the VirtIO disk
qm set $VMID --boot order=virtio0
# Attach a Cloud‑Init drive (recommended on UEFI systems using SCSI)
qm set $VMID --scsi1 $STORAGE:cloudinit
# Create a custom Cloud‑Init configuration snippet
cat << EOF | tee /var/lib/vz/snippets/ubuntu.yaml
#cloud-config
runcmd:
- apt-get update
- apt-get install -y qemu-guest-agent htop
- systemctl enable ssh
- reboot
EOF
# Apply Cloud‑Init customizations and additional VM settings
qm set $VMID --cicustom "vendor=local:snippets/ubuntu.yaml"
qm set $VMID --tags ubuntu-template,noble,cloudinit
qm set ${VMID} --ciuser ${CI_USER} --cipassword "${CI_PASSWORD}"
qm set $VMID --sshkeys ~/ssh.pub
qm set $VMID --ipconfig0 ip=dhcp
# Finally, convert the configured VM into a template for rapid future deployment
echo "Converting VM to template..."
qm template ${VMID}
echo "Template ${VM_NAME} (VMID: ${VMID}) created successfully."
Make It Your Own
Before you run the script there are some things you would need to modify to tailor it to your environment. Here’s a quick rundown of the main components you might consider editing:
# VM and storage identifiers
VMID=10002
STORAGE=local-lvm
# Define the VM name and Ubuntu image parameters
VM_NAME="ubuntu-2404-cloudinit-template"
STORAGE="local-lvm" # Adjust if using a different storage backend
IMAGE_URL="https://cloud-images.ubuntu.com/daily/server/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
IMAGE_PATH="/var/lib/vz/template/iso/$(basename ${IMAGE_URL})"
# Cloud‑Init credentials (consider stronger hashing for production)
CI_USER="ubuntu"
CI_PASSWORD="$(openssl passwd -6 ubuntu)"
# Create an SSH public key file for injecting into the VM
echo "ssh-rsa PASTE YOUR PUBLIC KEY HERE" > ssh.pub
- VM Identification & Naming: Customize the VMID and VM_NAME to assign a unique identifier and a descriptive name for your template.
- Storage Settings: The STORAGE variable lets you define the backend storage (e.g., local-lvm, local-zfs). Adjust this parameter based on your storage setup.
- Image Source & Path: The IMAGE_URL points to the cloud image you wish to use, while IMAGE_PATH determines where the image will be stored locally.
- Cloud‑Init Credentials: Parameters like CI_USER and CI_PASSWORD are set to pre-configure the default login credentials. Please modify these as per your requirements.
- SSH Key Injection: The script injects an SSH public key file for secure access to your VM, saving you the hassle of manually copying SSH keys after deployment. Please add your SSH key here echo "ssh-rsa PASTE YOUR PUBLIC KEY HERE" > ssh.pub. If you need to create a new SSH Key click here to learn how.
How to Use This Script
It’s really simple — just SSH into your Proxmox server, create a new file (e.g., create-ubuntu-2404-template.sh), paste the script into it, make it executable, and run it. The script handles everything for you, from downloading the Ubuntu image to converting the VM into a reusable template.
Here’s a step-by-step guide:
- SSH into Your Proxmox Server: ssh your_user@proxmox-server-ip
- Create the Script File: Use your preferred text editor to create a new file. For example, using Nano: nano create-ubuntu-2404-template.sh
- Paste the script into this file, then save and exit the editor. In Nano, press Ctrl+O to save, then Ctrl+X to exit.
- Make the Script Executable: Change the file permissions to make it executable: chmod +x create-ubuntu-2404-template.sh
- Run the Script: Execute the script with: ./create
That’s it! With these commands, your Proxmox server will automatically handle downloading the Ubuntu image, setting up the VM, integrating Cloud‑Init, and converting the VM into a reusable template.
Profit!
Awesome! So now you have a shiny template ready to be used. You can use this template to create as many VMs as you want and you will have a brand new VM up and running in minutes. Now let's see how to use this template to create a new VM.
Login to Proxmox and locate the template that you just created
In this example, I will use the ‘ubuntu-2404-cloudinit-k8s-template’. Right-click on the template and click Clone. Fill in the VM ID and Name and click Clone.
Once the cloning is complete, the new VM will appear in your list. You can modify the hardware settings if required, then click Start. The VM will boot up, and Cloud‑Init will automatically perform the initial configuration tasks.
And voila, the VM is created.
In a future tutorial, we will look at how to configure the Cloud-Init configuration to set things like a new user, password, static IP etc. and also I will teach you how to create a Kubernetes-ready template so you can quickly set up as many nodes as you wish.
Feel free to leave any feedback, suggestions or your thoughts down in the comments.
Thank you!




Top comments (0)