Introduction

Cloud-init is a powerful industry-standard tool for automating the initial configuration of virtual machines. Instead of manually setting up each new VM with network settings, user accounts, and SSH keys, cloud-init templates let you deploy pre-configured VMs in seconds.

This guide shows you how to create a cloud-init template VM in Proxmox that you can clone repeatedly, with each clone automatically configured with unique settings like IP addresses, hostnames, and SSH keys.

Why use cloud-init templates?

  • Speed: Deploy fully-configured VMs in under a minute
  • Consistency: Every VM starts with identical base configuration
  • Automation: Perfect for Infrastructure as Code (Terraform, Ansible)
  • Flexibility: Customize network, users, and SSH keys per-instance

Common use cases:

  • Kubernetes cluster nodes that need identical base configurations
  • Development/staging environments that mirror production
  • Test VMs that can be quickly created and destroyed
  • Automated CI/CD pipeline runners

Prerequisites

Before you begin, ensure you have:

  • Proxmox VE 6.x or later with root or administrative access
  • Storage space for the template VM (minimum 10GB recommended)
  • Ubuntu server ISO or cloud image (see note below)
  • Basic Linux command-line knowledge
  • SSH access to the Proxmox host

Proxmox versions tested: This guide works with Proxmox VE 6.x, 7.x, and 8.x

Recommended Ubuntu versions:

  • Ubuntu 20.04 LTS (Focal)
  • Ubuntu 22.04 LTS (Jammy)
  • Ubuntu 24.04 LTS (Noble)

Cloud Image vs. Installer ISO

Important distinction:

  • Cloud images (.img files): Pre-built, minimal OS images specifically designed for cloud-init. Recommended for this guide.
  • Installer ISOs: Traditional installation media requiring manual OS installation

For this guide, we’ll use a standard Ubuntu Server ISO, but you can adapt the process for cloud images. If using cloud images, skip Step 2 (OS installation) and import the image directly.


Part 1: Create and Configure the Base VM

Step 1: Create the Virtual Machine

We’ll create a new VM that will become our template. You can use either the Proxmox Web UI or CLI.

Using Proxmox Web UI:

  1. General Settings:
    • VM ID: Choose a high number to keep templates separate from regular VMs (e.g., 900)
    • Name: ubuntu-cloud-template or similar descriptive name
    • Resource Pool: (optional) Create a “Templates” pool for organization
  1. OS Settings:

    • ISO Image: Select your uploaded Ubuntu Server ISO (e.g., ubuntu-22.04-server-amd64.iso)
    • Guest OS Type: Linux
    • Version: Select the appropriate Ubuntu version
  2. System Settings:

    • BIOS: OVMF (UEFI) for modern systems, or SeaBIOS for compatibility
    • Machine Type: q35 (recommended) or i440fx (legacy)
    • SCSI Controller: VirtIO SCSI (for best performance)
    • Qemu Agent: Enable (important for cloud-init integration)
  3. Disks:

    • Bus/Device: SCSI 0 (recommended for performance)
    • Storage: Select your storage pool (e.g., local-lvm)
    • Disk Size: 10 GB minimum (expand after cloning if needed)
    • Cache: Default (No cache) or Write back for better performance
    • Discard: Enable (enables thin provisioning on SSD storage)
    • SSD Emulation: Enable if using SSD storage
  4. CPU:

    • Sockets: 1
    • Cores: 2 (minimum; can be increased when cloning)
    • Type: host (provides best performance by exposing all CPU features)
  5. Memory:

    • RAM: 2048 MB (2GB minimum for Ubuntu Server)
    • Ballooning Device: Disable for templates (prevents memory issues)
  6. Network:

    • Model: VirtIO (paravirtualized) for best network performance
    • Bridge: vmbr0 (your default bridge, adjust if using custom networking)
    • Firewall: Enable if desired
  7. Confirm:

    • Review all settings carefully
    • Click Finish to create the VM
    • Do not start the VM yet

Using Proxmox CLI:

qm create 900 \
  --name ubuntu-cloud-template \
  --memory 2048 \
  --cores 2 \
  --net0 virtio,bridge=vmbr0 \
  --scsihw virtio-scsi-pci \
  --scsi0 local-lvm:10 \
  --ide2 local:iso/ubuntu-22.04-server-amd64.iso,media=cdrom \
  --boot order=scsi0 \
  --agent enabled=1

Step 2: Install Ubuntu Server

  1. Start the VM:

    • From Proxmox UI, select the VM and click Start
    • Open the Console to access the VM
  2. Install Ubuntu:

    • Language: Select your preferred language
    • Keyboard: Choose your keyboard layout
    • Installation type: Ubuntu Server (minimized)
    • Network: Accept DHCP configuration (we’ll configure static IPs via cloud-init later)
    • Storage: Use entire disk with default partitioning
    • Profile setup:
      • Your name: ubuntu (this is temporary, cloud-init will manage users)
      • Server name: ubuntu-template
      • Username: ubuntu
      • Password: Set a secure password (you can disable this user later)
    • SSH: Install OpenSSH server (check this option)
    • Featured snaps: Leave unchecked (keep template minimal)
  3. Complete Installation:

    • Wait for installation to finish
    • When prompted, Reboot the VM
    • Let the VM boot into the fresh Ubuntu installation
  4. Update the System:

    ssh ubuntu@<vm-ip>
    sudo apt update && sudo apt upgrade -y
    

Part 2: Install and Configure Cloud-Init

Now we’ll install cloud-init and configure it for Proxmox’s cloud-init integration.

Step 3: Install Cloud-Init Package

sudo apt update
sudo apt install -y cloud-init qemu-guest-agent

Installed packages:

  • cloud-init: Handles initial VM configuration
  • qemu-guest-agent: Allows Proxmox to communicate with the VM

Step 4: Configure Cloud-Init Datasource

Cloud-init needs to know where to get its configuration. For Proxmox, we use the NoCloud datasource.

  1. Create or edit the datasource configuration:

    sudo nano /etc/cloud/cloud.cfg.d/99_pve.cfg
    
  2. Add the following content:

    datasource_list: [ NoCloud, ConfigDrive ]
    
  3. Save and exit (Ctrl+X, then Y, then Enter)


Step 5: Clean Existing Network Configuration

To allow cloud-init to fully manage networking, remove existing netplan configurations:

sudo rm -f /etc/netplan/*.yaml

Why this step?

  • Prevents conflicts between static network config and cloud-init
  • Allows each cloned VM to get unique networking from cloud-init
  • Ensures DHCP works initially if no cloud-init network config is provided

Step 6: Clean Cloud-Init State

Before converting to a template, clean cloud-init’s state so each clone starts fresh:

sudo cloud-init clean
sudo cloud-init clean --logs

What this does:

  • Removes instance-specific data from this VM
  • Clears cloud-init cache and logs
  • Ensures each clone runs cloud-init as if it’s a first boot

Step 7: Shut Down the VM

Before converting to a template, properly shut down the VM:

sudo shutdown -h now

Or from Proxmox shell:

qm shutdown 900

Wait for the VM to fully power off (status should show “stopped”).


Part 3: Configure Proxmox Cloud-Init Integration

Now we’ll add Proxmox-specific cloud-init features to the VM.

Step 8: Add Cloud-Init Drive

From the Proxmox shell (SSH into your Proxmox host), run:

qm set 900 --ide2 local-lvm:cloudinit

What this does:

  • Attaches a special cloud-init configuration drive as ide2
  • Proxmox uses this drive to pass cloud-init configuration to the VM
  • This drive appears in the VM’s hardware as a CD-ROM device

Verify: Check the VM’s Hardware tab in the Proxmox UI - you should see “CloudInit Drive (ide2)”


Step 9: Configure Boot Settings

Set proper boot order so the VM boots from the OS disk, not the cloud-init drive:

qm set 900 --boot order=scsi0 --bootdisk scsi0

Step 10: Configure SCSI Controller (if not already set)

Ensure the SCSI controller is set to VirtIO for best performance:

qm set 900 --scsihw virtio-scsi-pci

Step 11: Enable Hot-Plugging (Optional)

Allow hot-plugging of network, disk, and USB devices:

qm set 900 --hotplug network,disk,usb

Benefits:

  • Add/remove network interfaces without rebooting
  • Resize disks without downtime
  • Useful for dynamic cloud environments

Part 4: Convert to Template

Step 12: Convert VM to Template

Once everything is configured, convert the VM to a template:

qm template 900

What happens:

  • VM is marked as a template in Proxmox
  • VM can no longer be started directly
  • VM can only be cloned to create new VMs
  • Template is read-only (protects against accidental changes)

Verify: In the Proxmox UI, the VM should now have a template icon and show “Template” in its description.


Part 5: Clone and Configure New VMs

Now you can create new VMs from your template.

Step 13: Clone the Template

Using Proxmox Web UI:

  1. Right-click the template (VM 900)
  2. Select Clone
  3. Configuration:
    • Target node: Select destination Proxmox node
    • VM ID: Enter unique ID (e.g., 101)
    • Name: Descriptive name (e.g., web-server-01)
    • Mode:
      • Full Clone (recommended): Creates independent copy
      • Linked Clone: Faster but depends on template
    • Target Storage: Select where to store the new VM
  4. Click Clone

Using Proxmox CLI:

# Full clone (recommended)
qm clone 900 101 --name web-server-01 --full

# Linked clone (faster, but dependent on template)
qm clone 900 101 --name web-server-01

Step 14: Configure Cloud-Init Settings

After cloning, configure the instance-specific settings via cloud-init.

Using Proxmox Web UI:

  1. Select the newly cloned VM (e.g., VM 101)
  2. Go to HardwareCloudInit Drive
  3. Click “Edit” to configure:
    • User: admin (username for SSH login)
    • Password: Set a password (or leave blank to use SSH keys only)
    • DNS domain: example.local (optional)
    • DNS servers: 8.8.8.8 8.8.4.4 (or your preferred DNS)
    • SSH public key: Paste your public SSH key for password-less login
    • IP Config (net0):
      • IPv4: 192.168.1.100/24 (static IP)
      • Gateway: 192.168.1.1
      • OR select DHCP for automatic IP assignment
  1. Click OK to save

Important: Any changes to cloud-init settings require the VM to be powered off first.

Using Proxmox CLI:

# Configure basic settings
qm set 101 --ciuser admin --cipassword SecurePassword123

# Configure static IP networking
qm set 101 --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1

# Configure DNS
qm set 101 --nameserver 8.8.8.8 --searchdomain example.local

# Add SSH public key (recommended over passwords)
qm set 101 --sshkeys /root/.ssh/authorized_keys
# Or specify inline:
# qm set 101 --sshkeys "ssh-rsa AAAAB3NzaC1yc2EA... user@host"

Example realistic configuration:

# Web server with static IP
qm set 101 \
  --ciuser admin \
  --cipassword MySecurePass123 \
  --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1 \
  --nameserver 8.8.8.8 \
  --searchdomain mycompany.local \
  --sshkeys "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7H... admin@workstation"

Step 15: Start the Cloned VM

Start the new VM and let cloud-init apply the configuration:

qm start 101

First boot process:

  1. VM boots from the OS disk
  2. Cloud-init detects this is the first boot
  3. Cloud-init reads configuration from the CloudInit drive
  4. Cloud-init applies user, network, and SSH settings
  5. VM is ready for use (usually within 30-60 seconds)

Part 6: Verify and Test

Step 16: Verify Cloud-Init Worked

  1. Check VM IP address:

    • In Proxmox UI, look at the VM’s Summary tab
    • The IP address should match what you configured
    • Qemu guest agent must be running for IP to display
  2. SSH into the VM:

    If you configured SSH keys, you should log in without a password.

  3. Check cloud-init logs:

    sudo tail -f /var/log/cloud-init-output.log
    

    Look for:

    Cloud-init v. 24.1.3-0ubuntu1 finished at <timestamp>. Datasource DataSourceNoCloud
    
  4. Verify networking:

    ip addr show
    hostname
    

    Should show your configured IP address and hostname.

  5. Check cloud-init status:

    sudo cloud-init status
    

    Expected output:

    status: done
    

Troubleshooting

Issue: VM has no network connectivity

Causes:

  • Cloud-init network configuration didn’t apply
  • Incorrect gateway or netmask
  • Bridge configuration issue in Proxmox

Solutions:

  1. Check cloud-init logs: sudo cat /var/log/cloud-init.log
  2. Verify IP config in Proxmox CloudInit settings
  3. Try rebooting the VM: qm reboot 101
  4. Check Proxmox network bridge is up: ip link show vmbr0

Issue: Can’t SSH into VM (connection refused)

Causes:

  • SSH keys not configured correctly
  • Cloud-init user creation failed
  • Firewall blocking port 22

Solutions:

  1. Use Proxmox console to log in directly
  2. Check if user exists: cat /etc/passwd | grep admin
  3. Verify SSH is running: sudo systemctl status ssh
  4. Check SSH configuration: sudo sshd -T | grep -i permitroot
  5. Review cloud-init user-data: sudo cloud-init query userdata

Issue: Cloud-init doesn’t run on first boot

Causes:

  • Cloud-init wasn’t cleaned before template conversion
  • CloudInit drive not attached
  • Datasource configuration missing

Solutions:

  1. Verify CloudInit drive exists: Check VM Hardware tab for ide2
  2. Check datasource config exists: cat /etc/cloud/cloud.cfg.d/99_pve.cfg
  3. Re-clean cloud-init (if still mutable):
    sudo cloud-init clean --logs --reboot
    

Issue: Changes to cloud-init settings don’t apply

Cause: Cloud-init only runs on first boot. Subsequent boots skip cloud-init unless you manually trigger it.

Solutions:

  1. Force cloud-init to run again:

    sudo cloud-init clean
    sudo reboot
    
  2. For new changes: Shut down VM, modify cloud-init settings in Proxmox, then start VM


Advanced Configuration

Using Custom Cloud-Init User-Data

For more advanced configurations (installing packages, running scripts), you can provide custom user-data:

  1. Create a user-data file:

    #cloud-config
    package_update: true
    package_upgrade: true
    packages:
      - docker.io
      - git
      - htop
    runcmd:
      - systemctl enable docker
      - usermod -aG docker admin
    
  2. Save as user-data.yaml

  3. Apply to VM:

    qm set 101 --cicustom "user=local:snippets/user-data.yaml"
    

Note: This requires the snippets directory to be configured in Proxmox storage.


Next Steps

Now that you have a working cloud-init template, you can:

  1. Automate VM creation with Terraform:

    • Use the Proxmox Terraform provider
    • Define infrastructure as code
    • Deploy entire environments automatically
  2. Integrate with Ansible:

    • Use cloud-init to bootstrap Ansible connectivity
    • Automate post-deployment configuration
    • Manage fleet of VMs
  3. Create multiple templates:

    • Ubuntu 20.04, 22.04, and 24.04 variants
    • Different server roles (web, database, etc.)
    • Pre-configured software stacks
  4. Explore cloud images:

    • Download official Ubuntu cloud images
    • Import directly without OS installation
    • Faster template creation

Best Practices

Template Management:

  • Use high VM IDs (900+) to separate templates from regular VMs
  • Document template versions and creation dates
  • Regularly update templates with security patches
  • Test template changes on clones before converting to template

Security:

  • Always use SSH keys instead of passwords
  • Disable password authentication in SSH config
  • Keep cloud-init and qemu-agent updated
  • Review cloud-init logs after first boot

Networking:

  • Use static IPs for production servers
  • Use DHCP for development/test environments
  • Document IP allocation to avoid conflicts
  • Configure proper DNS for hostname resolution

  • How to resize VM disks in Proxmox
  • Automating Proxmox VM deployments with Terraform
  • Setting up Kubernetes clusters with cloud-init templates
  • Proxmox networking best practices

Last updated: December 2024