⚙ Proxmox · Cloud-Init

Deploying Ubuntu 24.04 LTS via Cloud-Init

Fully automated VM provisioning — no interactive installer, SSH-ready in under 2 minutes.

Proxmox VE Ubuntu 24.04 LTS cloud-init q35

Why Cloud Image over ISO

  • No interactive installer — fully automated via cloud-init
  • Cloud-init handles user creation, SSH key injection, network (DHCP), hostname
  • Boots to an SSH-accessible system in under 2 minutes
  • Image is reusable as a template base for cloning

Prerequisites

  • Proxmox VE host with at least one bridge (e.g. vmbr0)
  • Storage pool that supports disk images (e.g. local-lvm lvmthin)
  • SSH access to the Proxmox host as root
  • An SSH public key to inject

1 Gather Environment Info

Before creating anything, confirm what's available on the host.

# Next available VM ID
pvesh get /cluster/nextid

# Available storage pools
pvesm status

# Network bridges
cat /etc/network/interfaces | grep -E '^iface|^auto'

# Node name
hostname

2 Download the Ubuntu 24.04 Cloud Image

Pull the cloud image directly to the Proxmox ISO storage directory. Check for an existing copy first to avoid re-downloading ~600MB unnecessarily.

# Fetch cloud image to Proxmox ISO repository (skips download if already present)
wget -O /var/lib/vz/template/iso/noble-cloudimg-amd64.img \
    https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

3 Create the VM

# List existing VMs and their IDs
qm list

# Get the next available ID from Proxmox
pvesh get /cluster/nextid
# Create VM shell — hardware profile only, no disk or drives yet
qm create 100 \
    --name ollama \
    --memory 16384 \
    --cores 6 \
    --sockets 1 \
    --cpu host \
    --net0 virtio,bridge=vmbr0 \
    --ostype l26 \
    --machine q35 \
    --scsihw virtio-scsi-pci \
    --onboot 0

Parameter Notes

--cpu host
Exposes host CPU features to the VM. Best for performance. Avoid if live migration between nodes is needed.
--machine q35
Modern chipset with PCIe support. Required for GPU passthrough in Phase 2.
--onboot 0
VM does not auto-start when Proxmox boots. Change to 1 if desired.
--scsihw virtio-scsi-pci
Required controller type for attaching the scsi0 disk in the next step.

4 Import and Attach the Disk

# Import cloud image as VM disk
qm importdisk 100 /var/lib/vz/template/iso/noble-cloudimg-amd64.img local-lvm

# Attach as primary SCSI disk with discard and SSD flags
qm set 100 --scsi0 local-lvm:vm-100-disk-0,discard=on,ssd=1

# Resize to desired capacity (cloud image is ~3.5GB raw)
qm resize 100 scsi0 100G

5 Add Cloud-Init Drive

This creates a small ISO attached as a CD-ROM. The VM reads it on first boot to configure itself. Without it, cloud-init has no source for its user data.

# Attach cloud-init ISO to VM
qm set 100 --ide2 local-lvm:cloudinit

6 Configure Cloud-Init

The --sshkeys flag injects your public key into the VM's authorized_keys on first boot, enabling key-based SSH login without a password. Don't have a key pair yet? See SSH Public Key Authentication.

# Write SSH public key to temp file
echo "{YOUR_PUBKEY}" > /tmp/vm-key.pub

# Apply cloud-init settings
qm set 100 \
    --ciuser {username} \
    --cipassword '{YOUR_PASSWORD}' \
    --sshkeys /tmp/vm-key.pub \
    --ipconfig0 ip=dhcp \
    --nameserver 8.8.8.8 \
    --searchdomain local

# Clean up
rm /tmp/vm-key.pub

7 Boot Configuration

# Set boot order to primary disk
qm set 100 --boot order=scsi0

# Add serial console (required for cloud-init output + noVNC serial tab)
qm set 100 --serial0 socket --vga serial0

8 Start the VM

# Start the VM — cloud-init runs on first boot and configures the system
qm start 100

9 Find the VM's IP Address

The VM gets its IP via DHCP from the network router — not from Proxmox. Proxmox does not track DHCP leases. A subnet ping-sweep populates the ARP table so you can grep for the VM's MAC address.

# Ping-sweep the subnet to populate the ARP table
for i in $(seq 1 254); do ping -c1 -W1 192.168.X.$i > /dev/null 2>&1 & done
wait

# Look up the VM's MAC address (shown in: qm config 100, under net0)
arp -an | grep -i "BC:24:11:XX:XX:XX"

10 Connect via SSH

# First connection — suppress host key prompt since this is a fresh VM
ssh -o StrictHostKeyChecking=no {username}@{VM_IP}

Add a permanent alias to ~/.ssh/config on the source Mac:

# Add to ~/.ssh/config for a persistent alias — then just: ssh ollama
Host ollama
    HostName {VM_IP}
    User {username}
    IdentityFile ~/.ssh/id_rsa

Then connect with ssh ollama. For a full walkthrough of key generation, distribution, and disabling password auth, see SSH Public Key Authentication.

11 Create Your User Account

If cloud-init created a default ubuntu user and you want a named account instead, this block creates the user, sets a password, installs your SSH public key, locks down the .ssh directory permissions, and grants passwordless sudo:

# Create the user with a home directory and bash shell, add to sudo group
sudo useradd -m -s /bin/bash -G sudo {username}

# Set the user's password
echo '{username}:{YOUR_PASSWORD}' | sudo chpasswd

# Create .ssh directory and install your public key for key-based login
sudo mkdir -p /home/{username}/.ssh
echo "{YOUR_PUBKEY}" | sudo tee /home/{username}/.ssh/authorized_keys

# Lock down .ssh permissions — SSH will reject keys if these are too open
sudo chmod 700 /home/{username}/.ssh
sudo chmod 600 /home/{username}/.ssh/authorized_keys
sudo chown -R {username}:{username} /home/{username}/.ssh

# Grant passwordless sudo
echo '{username} ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/{username}

Final VM Configuration Reference

Setting Value
VM ID 100
Name ollama
CPU 6 cores, host type
RAM 16384 MB
Disk 100G, local-lvm, discard+ssd
Machine q35
Network virtio, vmbr0, DHCP
Cloud-init drive ide2
Serial console serial0 socket
Auto-start disabled (onboot 0)

Notes

ssh root@{PROXMOX_IP} "command" | python3 -c \
  "import sys,re; [print(re.sub(r'\x1b\[[0-9;]*[A-Za-z]','',l).strip()) for l in sys.stdin if l.strip()]"

DHCP Discovery

If the VM's IP isn't showing up via ARP, the lease is held by the network router, not Proxmox. The ping-sweep in Step 9 populates the ARP table so you can grep for the MAC. The MAC is visible in qm config 100 under net0.

Re-running Cloud-Init

Cloud-init only runs on first boot. To re-run it after changing settings:

sudo cloud-init clean && sudo reboot

Changelog

  • 2026-03-18 — Initial deployment documented