Proxmox VM Creation, Templates and Cloud-Init
Complete guide to creating virtual machines, templates, and using cloud-init for automated provisioning in Proxmox VE.
# Proxmox VM Creation, Templates and Cloud-Init
Introduction
Creating virtual machines efficiently is a core skill for Proxmox VE administrators. Rather than manually installing each VM from an ISO, administrators can use VM templates and cloud-init to rapidly provision standardized, pre-configured virtual machines. This approach saves time, ensures consistency, and reduces configuration errors.
This tutorial covers VM creation from ISOs, cloning VMs to templates, deploying VMs from templates, and using cloud-init for automated first-boot configuration including user accounts, SSH keys, network settings, and package installation.
By the end of this guide, you will be able to rapidly provision VMs using templates and cloud-init in Proxmox VE.
Technical Background
VM Creation Methods
| Method | Description | Best For | |--------|-------------|----------| | ISO installation | Install OS from ISO image | Custom OS, one-off VMs | | Clone from template | Clone a pre-configured VM | Rapid deployment of standard VMs | | Cloud-init | Automated first-boot config | Standardized, repeatable provisioning | | Linked clone | Share base disk with snapshots | Space-efficient VM sprawl |
Cloud-Init
Cloud-init is the industry standard for multi-distribution cloud instance initialization. It handles early initialization of cloud instances including:
- Setting hostname
- Configuring network interfaces
- Installing packages
- Running user scripts (runcmd)
- Setting up users and SSH keys
- Growing root filesystem
In Proxmox, cloud-init is implemented via a special drive (ide2 or sata0) containing configuration that the guest OS cloud-init service reads on first boot.
VM Templates
A Proxmox template is a VM marked as a template that cannot be started directly. Cloning a template creates a full or linked copy that can be customized and started.
Prerequisites
- Proxmox VE installed and operational
- Storage configured (local, local-lvm, or shared)
- OS ISO images uploaded to storage
- qemu-guest-agent installed in template VMs (recommended)
- Understanding of Linux networking and user administration
Architecture
VM Provisioning Workflow
ISO Installation → Configure VM → Install Guest Agent → Convert to Template → Clone → Customize → Startgraph TD
A[Download ISO] --> B[Create VM from ISO]
B --> C[Install Guest OS]
C --> D[Install qemu-guest-agent]
D --> E[Configure and Update]
E --> F[Convert to Template]
F --> G[Clone Template to VM]
G --> H[Apply cloud-init config]
H --> I[Start VM]Cloud-Init Drive Architecture
Proxmox Host
├── VM Configuration (vm-100.conf)
├── VM Disk (vm-100-disk-0)
└── Cloud-Init Drive (vm-100-cloudinit)
├── userdata (user config)
├── metadata (instance ID, hostname)
└── network-config (network settings)Step-by-Step Implementation
Step 1: Create VM from ISO
**Purpose**: Install a guest OS from an ISO image as the base for a template.
**Commands**:
# Create VM with 2 vCPUs, 4 GB RAM, 32 GB disk
qm create 9000 --name ubuntu-template --memory 4096 --cores 2 \
--net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci
# Set boot order to CD-ROM first
qm set 9000 --boot order=ide2 --ide2 local:iso/ubuntu-24.04.iso,media=cdrom
# Add SCSI disk
qm set 9000 --scsi0 local-lvm:32
# Enable QEMU guest agent
qm set 9000 --agent 1
# Start VM
qm start 9000**Configuration files**: `/etc/pve/qemu-server/9000.conf`
**Explanation**: VM ID 9000 is in the template range (9000-9999 convention). `virtio-scsi-pci` provides best disk performance. QEMU guest agent enables host-to-guest communication for shutdown, IP detection, and filesystem freezing.
**Expected output**: VM created and started; ISO boots for OS installation.
**Verification**:
qm list
# Shows VM 9000 with status "running"Step 2: Install Guest OS and Configure
**Purpose**: Install the operating system and prepare it for templating.
**Procedure** (via Proxmox console or VNC):
1. Boot from ISO and install Ubuntu Server 2. During installation, create a default user (e.g., `ubuntu`) 3. After installation, log in and update packages:
sudo apt update && sudo apt upgrade -y4. Install QEMU guest agent:
sudo apt install qemu-guest-agent -y
sudo systemctl enable --now qemu-guest-agent5. Install cloud-init:
sudo apt install cloud-init -y6. Configure SSH and disable password authentication (optional):
sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no
# PubkeyAuthentication yes
sudo systemctl restart ssh7. Clean machine-specific identifiers:
sudo rm /etc/machine-id
sudo rm /var/lib/dbus/machine-id
sudo truncate -s 0 /etc/machine-id8. Shut down the VM:
sudo poweroff**Expected output**: VM installed with guest agent and cloud-init ready.
**Verification**:
qm agent 9000 ping
# Should return OK if guest agent is respondingStep 3: Convert VM to Template
**Purpose**: Mark the configured VM as a template for cloning.
**Commands**:
# Convert VM 9000 to template
qm template 9000
# Verify template status
qm list
qm config 9000 | grep template**Explanation**: Templates cannot be started directly. They serve as read-only base images for cloning. The VM configuration remains intact but the VM is locked against starting.
**Expected output**: VM 9000 shows as template in `qm list`.
**Verification**:
qm list
# Shows VM 9000 with status "template"Step 4: Clone Template to New VM
**Purpose**: Deploy a new VM from the template.
**Commands**:
# Full clone (independent copy)
qm clone 9000 100 --name web-server-01 --full
# Linked clone (shares base disk, copy-on-write)
qm clone 9000 101 --name web-server-02 --linked
# Set network and start
qm set 100 --net0 virtio,bridge=vmbr0
qm start 100**Explanation**: Full clones are independent but use more storage. Linked clones share the template's disk and only store changes, saving space but depending on the template existing.
**Expected output**: New VMs created from template with unique IDs and names.
**Verification**:
qm list
# Shows VMs 100 and 101 with status "stopped"Step 5: Configure Cloud-Init for Automated Provisioning
**Purpose**: Automate first-boot configuration using cloud-init.
**Commands**:
# Set cloud-init parameters on VM 100
qm set 100 --ciuser ubuntu --sshkeys ~/.ssh/id_rsa.pub \
--ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1 \
--nameserver 8.8.8.8 --searchdomain ityantra.lab
# Verify cloud-init config
qm config 100 | grep -i ci
# Start VM (cloud-init runs on first boot)
qm start 100
# Check cloud-init status in guest
qm agent 100 ping**Explanation**: `--ciuser` sets the default user, `--sshkeys` injects SSH public keys, `--ipconfig0` configures static IP on the first network interface, `--nameserver` and `--searchdomain` configure DNS.
**Expected output**: VM boots with pre-configured user, SSH access, and network settings without manual intervention.
**Verification**:
# SSH into the VM
ssh ubuntu@192.168.1.100Configuration Examples
Cloud-Init Configuration Examples
# Basic cloud-init setup
qm set 100 --ciuser admin --sshkeys /home/admin/.ssh/id_rsa.pub
# Static IP configuration
qm set 100 --ipconfig0 ip=10.1.10.50/24,gw=10.1.10.1
# DNS configuration
qm set 100 --nameserver 10.1.10.10 --searchdomain ityantra.lab
# Custom user data (advanced)
qm set 100 --cicustom user=local:snippets/userdata.yaml
# Disk resize before clone
qm resize 100 scsi0 +20GCloud-Init User Data Example (`userdata.yaml`)
#cloud-config
users:
- name: admin
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-rsa AAAAB3... user@example.com
packages:
- nginx
- git
- vim
runcmd:
- systemctl enable nginx
- systemctl start nginx
- echo "Provisioned by cloud-init" > /etc/motd
write_files:
- path: /etc/nginx/sites-available/default
content: |
server {
listen 80;
server_name _;
root /var/www/html;
}VM Creation Script Example (`create-vm.sh`)
#!/bin/bash
TEMPLATE_ID=9000
NEW_ID=$1
VM_NAME=$2
VM_IP=$3
qm clone $TEMPLATE_ID $NEW_ID --name $VM_NAME --full
qm set $NEW_ID --ciuser ubuntu --sshkeys ~/.ssh/id_rsa.pub
qm set $NEW_ID --ipconfig0 ip=$VM_IP/24,gw=192.168.1.1
qm set $NEW_ID --nameserver 8.8.8.8 --searchdomain ityantra.lab
qm start $NEW_ID
echo "VM $VM_NAME ($NEW_ID) created with IP $VM_IP"Complete VM Configuration Example (`/etc/pve/qemu-server/100.conf`)
agent: 1
boot: order=virtio0
ciuser: ubuntu
cores: 2
memory: 4096
meta: creation-time=1700000000,template=0
name: web-server-01
net0: virtio=AA:BB:CC:DD:EE:FF,bridge=vmbr0
ipconfig0: ip=192.168.1.100/24,gw=192.168.1.1
nameserver: 8.8.8.8
searchdomain: ityantra.lab
sshkeys: ssh-rsa AAAAB3...
ostype: l26
scsihw: virtio-scsi-pci
smbios1: uuid=...
virtio0: local-lvm:vm-100-disk-0,size=32GVerification
CLI Commands
qm list -- List all VMs and templates
qm config <vmid> -- Show VM configuration
qm config <vmid> | grep ci -- Show cloud-init settings
qm agent <vmid> ping -- Check guest agent status
qm status <vmid> -- Show VM status
qm showcmd <vmid> -- Show QEMU command lineWeb Interface Verification
- Templates show with "Template" icon
- Cloud-init tab shows configured parameters
- VM status shows "running" after start
- Console shows cloud-init execution messages
Expected Results
- Template VM cannot be started (locked)
- Cloned VMs boot with cloud-init configuration
- SSH access works with injected keys
- Static IP configured automatically
- Guest agent responds to pings
Troubleshooting
| Symptom | Possible Cause | Diagnostic Command | Corrective Action | Verification | |---------|---------------|-------------------|-------------------|--------------| | Cannot start template | Template is locked | `qm status <vmid>` | Clone template first; don't start template | Clone succeeds | | Cloud-init not running | cloud-init not installed in guest | `qm agent <vmid> ping` | Install cloud-init package in template | cloud-init executes | | SSH key not injected | Wrong user or key format | `qm config <vmid> | grep sshkeys` | Verify `--ciuser` and `--sshkeys` path | SSH login works | | Static IP not applied | Network config missing | `ip addr show` in guest | Verify `--ipconfig0` syntax | IP configured | | Clone fails - disk busy | Template has snapshots | `qm listsnapshot <vmid>` | Remove snapshots or use full clone | Clone succeeds | | Guest agent not responding | Agent not installed or not running | `systemctl status qemu-guest-agent` | Install and start qemu-guest-agent | Agent responds | | VM won't boot from cloned disk | Boot order wrong | `qm config <vmid> | grep boot` | Set correct boot order | VM boots | | Linked clone depends on template | Template deleted or moved | `qm config <clone> | grep parent` | Keep template available or use full clone | Clone independent |
Best Practices
1. **Use VM ID range 9000-9999 for templates**: Makes templates easy to identify. 2. **Install QEMU guest agent in all templates**: Enables graceful shutdown, IP detection, and guest commands. 3. **Use cloud-init for all standard VMs**: Ensures consistent configuration and reduces manual setup. 4. **Keep templates updated**: Regularly update template OS and convert to new template version. 5. **Use full clones for production**: Independent disks avoid template dependency issues. 6. **Use linked clones for testing**: Save storage space for temporary or test VMs. 7. **Document cloud-init parameters**: Record standard user, SSH keys, and network settings. 8. **Test template cloning regularly**: Verify templates work after updates. 9. **Use descriptive VM names**: Include environment, role, and number (e.g., `web-prod-01`). 10. **Backup templates**: Include templates in backup schedules.
Conclusion
Proxmox VM creation with templates and cloud-init enables rapid, standardized virtual machine provisioning. By creating a well-configured template with QEMU guest agent and cloud-init, administrators can deploy new VMs in seconds with consistent user accounts, SSH keys, network settings, and automated package installation. This approach scales from single-node deployments to multi-node clusters, reducing operational overhead and configuration drift.
References
- Proxmox VE VM Management (https://pve.proxmox.com/wiki/Virtual_Machines)
- Proxmox VE Templates and Clones (https://pve.proxmox.com/wiki/Template_Clone)
- Proxmox VE Cloud-Init (https://pve.proxmox.com/wiki/Cloud-Init)
- Cloud-Init Documentation (https://cloudinit.readthedocs.io/)
- QEMU Guest Agent (https://wiki.qemu.org/Features/GuestAgent)
- Proxmox VE qm Command Reference (https://pve.proxmox.com/pve-docs/qm.1.html)