Skip to main content
RunBook Academy

← All labs in Proxmox VE

Lab · foundation · ~45 min

Build a hardened LXC container with cloud-init and SSH keys

A · Physical hardwareB · Nested virtualisationC · Simulation

Objectives

  • Create a reusable cloud-init template from an Ubuntu cloud image
  • Convert it to a PVE template
  • Clone and customise on deploy (hostname, IP, SSH keys)
  • Harden SSH: disable password auth, restrict root login

Prerequisites

  • A PVE host or cluster
  • Internet access to download a cloud image
  • A storage pool that supports templates (local-lvm, ZFS, Ceph)

Cloud-init template + hardened deploy

This lab produces a reusable, hardened cloud-init template you can clone into dozens of identical containers in seconds.

Steps

1. Download a cloud image

cd /tmp
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

2. Create the base VM shell

qm create 9000 --name ubuntu-template --memory 1024 --cores 2 \
  --net0 virtio,bridge=vmbr0 --ostype l26 --scsihw virtio-scsi-single

3. Import the cloud image as a disk

qm importdisk 9000 /tmp/noble-server-cloudimg-amd64.img local-lvm -format qcow2
qm set 9000 --scsi0 local-lvm:vm-9000-disk-0,iothread=1 --boot order=scsi0

4. Add the cloud-init drive

qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --serial0 socket --vga serial0
qm set 9000 --agent enabled=1

5. Configure boot-time customisation

qm set 9000 --ipconfig0 ip=dhcp
qm set 9000 --ciuser ubuntu
qm set 9000 --sshkeys ~/.ssh/id_rsa.pub
qm set 9000 --ciupgrade packages

6. Boot and inspect (optional)

qm start 9000
# Wait for the VM to boot
qm guest exec 9000 hostnamectl
qm stop 9000

7. Convert to template

In the GUI: right-click the VM → “Convert to template”.

Or via CLI:

qm template 9000

8. Clone the template to a deployable VM

qm clone 9000 100 --name web-01 --full 0
qm set 100 --ipconfig0 ip=10.0.0.50/24,gw=10.0.0.1
qm set 100 --ciuser deployer
qm set 100 --sshkeys ~/.ssh/deployer.pub
qm start 100

9. Verify the deploy

ssh deployer@10.0.0.50
# Should log in with your key, no password prompt

10. Harden SSH inside the deployed VM

# Inside the VM
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sudo systemctl restart sshd

# Verify
sudo sshd -T | grep -E 'passwordauth|permitroot'
# Expected: passwordauthentication no; permitrootlogin prohibit-password

If you want every clone to inherit hardening, do step 10 inside the template VM before converting it, and add a firstboot script:

# Inside the template VM, before qm template
cat > /etc/cloud/cloud.cfg.d/99-hardening.cfg << 'EOF'
runcmd:
  - sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
  - systemctl restart sshd
EOF

Then re-convert:

qm stop 9000
qm template 9000

Verification

  • qm list shows the template
  • Cloned VMs inherit your SSH keys, hostname, and IP
  • Password SSH is disabled on every clone
  • All clones share the same disk-image footprint (linked clone)

Cleanup

qm stop 100 && qm destroy 100
qm destroy 9000

Deliverables

  • · A named template (e.g., ubuntu-2404-cloudinit-template)
  • · A cloned LXC or VM with your SSH key and a unique hostname/IP
  • · SSH access with key only, password auth disabled

Verification status

Executed end to end
not yet run on hardware

The commands and configuration here have been reviewed against the verified software versions, but nobody has run this lab start to finish on a system meeting its prerequisites. Treat the Expected Outcome as the intended result rather than an observed one, and keep the Cleanup section to hand.