VOOZH about

URL: https://dev.to/davidtio/boot-in-seconds-cloud-images-cloud-init-in-podman-2ki0

⇱ Boot in Seconds: Cloud Images + cloud-init in Podman - DEV Community


Boot in Seconds: Cloud Images + cloud-init

Quick one-liner: Skip the installer entirely β€” download a pre-built cloud image, seed it with cloud-init, and boot a fully configured VM in seconds.


πŸ’‘ Why This Matters

Post #3 proved persistence works, but that interactive Alpine install took time. Download ISO, boot, run installer, answer prompts, wait, configure. Repeat that for every new VM and you'll spend more time installing than actually using them.

Cloud images solve this. They're pre-built disk images with an OS already installed. Ubuntu, Fedora, Debian, Rocky β€” they all publish ready-to-boot images. You download one, tell cloud-init your SSH key and username, and boot. No installer, no prompts, no waiting.

This post swaps the manual install for a cloud image. You'll download an Ubuntu cloud image, create a cloud-init seed, and boot straight into a configured VM.


πŸ“‹ Prerequisites

  • qemu:base image from Post #1
  • ~/vm directory from Post #3
  • genisoimage installed on your host (provides the mkisofs command)

πŸ“₯ Step 1: Download a Cloud Image

Ubuntu publishes cloud images for every release. Grab the latest LTS (24.04 Noble Numbat):

$ cd ~/vm
$ curl -O https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
$ ls -lh noble-server-cloudimg-amd64.img
-rw-rw-r-- 1 user user 650M Apr 1 12:00 noble-server-cloudimg-amd64.img

That 650 MB file is a complete Ubuntu 24.04 installation, ready to boot. No install needed.

πŸ—ΊοΈ Where to Find Cloud Images

Most major Linux distributions publish cloud images. Here's where to get them:

Open Source Distributions

Enterprise Distributions

Distribution Cloud Image Repository Access
RHEL https://access.redhat.com/downloads/content/rhel Subscription (free developer tier)
SLES https://download.suse.com/ Account required (free trial available)
Oracle Linux https://yum.oracle.com/oracle-linux-templates.html Free
Amazon Linux https://docs.aws.amazon.com/linux/al2023/ug/outside-ec2-download.html Free

Format tips:

  • Look for qcow2 format β€” it's thin-provisioned and works best with QEMU/KVM
  • Some sites offer raw images (.img) β€” these work too but take more disk space
  • Avoid VMDK or VDI formats β€” those are for VMware and VirtualBox

Note: Alpine Linux offers cloud images but uses dynamic URLs based on provider, architecture, and firmware options. Visit https://alpinelinux.org/cloud/ to generate the correct download link.


βš™οΈ Step 2: Create a cloud-init Seed

cloud-init is the standard for first-boot VM configuration. It runs on the first boot, reads a small YAML file, and sets up users, SSH keys, hostnames, packages β€” whatever you tell it to.

πŸ”‘ Get Your SSH Public Key

Cloud images don't have passwords by default β€” they use SSH key authentication. You'll need a key pair to log in.

Check if you already have one:

$ ls -la ~/.ssh/id_ed25519.pub

If you see a file, skip ahead β€” you're set. If not, generate one. ed25519 is the modern standard: faster, smaller, and more secure than RSA:

$ ssh-keygen -t ed25519 -C "your-email@example.com"

Press Enter to accept the default location (~/.ssh/id_ed25519). Optionally set a passphrase for extra security.

Then grab your public key:

$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...your-key-here... your-email@example.com

Copy the entire line β€” you'll paste it into the user-data file below.

πŸ”’ Creating a Hashed Password

Cloud images accept passwords in two formats: plain text (risky) or hashed (secure). We'll use a SHA-512 hash. Use read -s to enter your password without it appearing in shell history, then pipe it to openssl:

$ read -s -p "Password: " PW && echo && openssl passwd -6 "$PW" && unset PW
$6$randomsalt$hashedpasswordstring...

The -6 flag means SHA-512. The output starts with $6$ β€” that's the identifier. Copy the entire string.

πŸ“ Build the user-data File

Create the user-data file for Ubuntu:

$ mkdir -p noble
$ cat > noble/user-data << 'EOF'
#cloud-config
preserve_hostname: false
hostname: kvmpodman
users:
 - name: sysadmin
 groups: sudo
 shell: /bin/bash
 ssh_authorized_keys:
 - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...your-key-here...
 lock_passwd: false
 passwd: '$6$randomsalt$your-hashed-password...'
runcmd:
 - echo "cloud-init completed" > /home/sysadmin/.cloud-init-done
EOF

Replace the SSH key with your own (cat ~/.ssh/id_ed25519.pub) and the passwd hash with the one you generated above.

This seed:

  • Sets the hostname to kvmpodman
  • Creates a user named sysadmin with sudo access
  • Enables password login (for console testing)
  • Leaves a marker file when cloud-init finishes

πŸ“„ Create meta-data

Create an empty meta-data file (required, but can be empty):

$ touch noble/meta-data

πŸ’Ώ Step 3: Build the cloud-init ISO

cloud-init reads its config from a CD-ROM attached to the VM. Use mkisofs to create a small ISO containing your seed files. Run this on your host, from ~/vm:

$ mkisofs -J -R -input-charset utf-8 -V cidata -o cloud-init-noble.iso noble/user-data noble/meta-data

You should see:

Total translation table size: 0
Total rockridge attributes bytes: 331
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
182 extents written (0 MB)

The -J flag enables Joliet extensions, -R adds Rock Ridge (Unix file permissions β€” this fixes the warning), and -V cidata sets the volume label to cidata β€” which is what cloud-init scans for on boot.

That small ISO contains your entire first-boot configuration.


πŸš€ Step 4: Boot the Cloud Image

Attach both the cloud image disk and the cloud-init ISO. The cloud image boots as normal, cloud-init detects the CD-ROM, and applies your seed on first boot:

$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/noble-server-cloudimg-amd64.img,format=qcow2 \
 -cdrom /vm/cloud-init-noble.iso \
 -boot c

The VM boots. Wait about 15-30 seconds for cloud-init to run. You'll see output like:

cloud-init 25.3-0ubuntu1~24.04.1 running 'modules:config' at Sat, 12 Apr 2026 12:34:56 +0000
cloud-init 25.3-0ubuntu1~24.04.1 running 'modules:final' at Sat, 12 Apr 2026 12:34:58 +0000
cloud-init 25.3-0ubuntu1~24.04.1 finished at Sat, 12 Apr 2026 12:35:02 +0000

Once you see finished, the VM is ready. Log in with the username and password you set:

kvmpodman login: sysadmin
Password:

Then shut down cleanly:

sysadmin@kvmpodman:~$ sudo poweroff

The container will exit automatically when the VM powers off, and the disk image persists.


βœ… Step 5: Verify cloud-init Ran

Boot again, this time without the cloud-init ISO (it's only needed on first boot):

$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/noble-server-cloudimg-amd64.img,format=qcow2

Log in with sysadmin and the password you set. Then verify:

sysadmin@kvmpodman:~$ sudo su -
[sudo] password for sysadmin: 
root@kvmpodman:~#

Check the disk layout:

root@kvmpodman:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 3.5G 0 disk 
β”œβ”€sda1 8:1 0 2.5G 0 part /
β”œβ”€sda14 8:14 0 4M 0 part 
β”œβ”€sda15 8:15 0 106M 0 part /boot/efi
└─sda16 259:0 0 913M 0 part /boot

The cloud image is thin-provisioned β€” the disk is 3.5 GB total, with a 2.5 GB root partition, 913 MB for /boot, and 106 MB for EFI. The 4 MB sda14 is a BIOS boot partition (GRUB uses it on non-EFI boots). No swap is configured by default.

Memory-wise, this VM was given 1 GB (-m 1024), and Ubuntu reports about 961 MB available after kernel reservations.

Confirm cloud-init ran:

root@kvmpodman:~# cloud-init status
status: done

Then power off:

root@kvmpodman:~# poweroff

The container exits automatically when the VM shuts down, and the disk image persists.


πŸ§ͺ Step 6: Mini Shootout β€” SLES 16 and Amazon Linux 2023

Cloud images skip the installer entirely, and cloud-init automates the rest of the setup. A working VM boots in seconds:

Method Time to Working VM
Manual install (Post #3) 5-10 minutes
Cloud image + cloud-init (IDE/SATA) ~25s
Cloud image + cloud-init (VirtIO) ~10s

Ubuntu is the easy path. With a working VM in about 10 seconds, we have plenty of time to spin up different distributions and see how they compare. Let's try two I find interesting:

SLES 16 β€” enterprise Linux that rarely gets covered in tutorials. Most blog posts stop at RHEL or CentOS. SLES is what shops with a SUSE subscription actually run, and it's almost nowhere to be found in container or KVM content.

Amazon Linux 2023 β€” I know it exists, but I've never used it outside an EC2 instance. It's built exclusively for AWS, so booting it as a native KVM VM on my own hardware is something I've been curious about.

We'll download both, build cloud-init seeds for each, and boot with VirtIO.

πŸ“₯ Download the Images

Head to the links in the table above and grab the qcow2 images for your distro:

  • SLES 16: SUSE Download Center β€” requires a free SUSE account. Look for SLES-16.0-Minimal-VM.x86_64-Cloud-GM.qcow2
  • Amazon Linux 2023: AWS Documentation β€” free, no account needed. Look for al2023-kvm-2023.10.20260325.0-kernel-6.1-x86_64.xfs.gpt.qcow2

Drop both files into ~/vm/.

βš™οΈ Build cloud-init Seeds

Create a directory for each distro:

$ mkdir -p sles16 al2023

SLES 16 β€” uses wheel group for sudo. SLES comments out %wheel in /etc/sudoers by default, so we need to uncomment it:

$ cat > sles16/user-data << 'EOF'
#cloud-config
preserve_hostname: false
hostname: kvmpodman
users:
 - name: sysadmin
 groups: wheel
 shell: /bin/bash
 ssh_authorized_keys:
 - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...your-key-here...
 lock_passwd: false
 passwd: '$6$randomsalt$your-hashed-password...'
runcmd:
 - sed -i 's/^#\s*%wheel\s*ALL=(ALL)\s*ALL\s*$/%wheel ALL=(ALL) ALL/' /etc/sudoers
 - echo "cloud-init completed" > /home/sysadmin/.cloud-init-done
EOF
$ touch sles16/meta-data

Amazon Linux 2023 β€” also uses wheel:

$ cat > al2023/user-data << 'EOF'
#cloud-config
preserve_hostname: false
hostname: kvmpodman
users:
 - name: sysadmin
 groups: wheel
 shell: /bin/bash
 ssh_authorized_keys:
 - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...your-key-here...
 lock_passwd: false
 passwd: '$6$randomsalt$your-hashed-password...'
runcmd:
 - echo "cloud-init completed" > /home/sysadmin/.cloud-init-done
EOF
$ touch al2023/meta-data

The SLES seed uses wheel and uncommented %wheel in /etc/sudoers. Amazon Linux also uses wheel but doesn't need the sed hack. The runcmd just leaves a marker file.

Build the ISOs on your host, from ~/vm:

$ mkisofs -J -R -input-charset utf-8 -V cidata -o cloud-init-sles.iso sles16/user-data sles16/meta-data
$ mkisofs -J -R -input-charset utf-8 -V cidata -o cloud-init-al2023.iso al2023/user-data al2023/meta-data

πŸš€ Boot

$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/SLES-16.0-Minimal-VM.x86_64-Cloud-GM.qcow2,format=qcow2,if=virtio \
 -cdrom /vm/cloud-init-sles.iso \
 -boot c
$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/al2023-kvm-2023.10.20260325.0-kernel-6.1-x86_64.xfs.gpt.qcow2,format=qcow2,if=virtio \
 -cdrom /vm/cloud-init-al2023.iso \
 -boot c

The first boot with the cloud-init ISO configures the VM. After that, drop the -cdrom flag β€” it's only needed once:

$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/SLES-16.0-Minimal-VM.x86_64-Cloud-GM.qcow2,format=qcow2,if=virtio
$ podman run --rm -it \
 --device /dev/kvm \
 -v ~/vm:/vm:z \
 qemu:base \
 qemu-system-x86_64 \
 -enable-kvm -cpu host \
 -nographic \
 -m 1024 \
 -drive file=/vm/al2023-kvm-2023.10.20260325.0-kernel-6.1-x86_64.xfs.gpt.qcow2,format=qcow2,if=virtio

πŸ“Š Results

Distribution Disk Size Boot Time Root Usage Notes
Ubuntu 24.04 3.5 GB ~10s 72% Lightweight server, ext4
SLES 16 1.3 GB ~10s 97% Minimal install, xfs, very tight on disk
Amazon Linux 2023 25 GB ~20s 7% Cloud-optimized, xfs, plenty of room

🦎 SLES 16 β€” What's Different

SLES boots fine with its own cloud-init seed. The wheel group works for sudo. Boot time:

real 0m9.855s
user 0m0.085s
sys 0m0.055s

About 10 seconds β€” same ballpark as Ubuntu. QEMU defaults to the right boot device when there's only one disk, so -boot c isn't needed for subsequent boots.

sysadmin@kvmpodman:~> cloud-init --version
/usr/bin/cloud-init 25.1.3-160000.1.2

The disk layout tells a different story:

sysadmin@kvmpodman:~> lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sr0 11:0 1 364K 0 rom 
vda 253:0 0 1.3G 0 disk 
β”œβ”€vda1 253:1 0 2M 0 part 
β”œβ”€vda2 253:2 0 512M 0 part /boot/efi
└─vda3 253:3 0 806M 0 part /

Three partitions instead of four β€” no separate /boot, just EFI and root. The 2 MB vda1 is the BIOS boot partition. The root filesystem is xfs, not ext4.

And the disk is already 97% full:

sysadmin@kvmpodman:~> df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/vda3 xfs 742M 716M 27M 97% /
/dev/vda2 vfat 512M 3.9M 508M 1% /boot/efi

27 MB of free space on root is tight. Installing a single package with zypper will likely fill the remaining space. Ubuntu gave you 688 MB free on a 3.5 GB image. SLES is a true minimal image β€” but that means almost no headroom.

Memory-wise, both Ubuntu and SLES report about 960 MB from the 1 GB allocation β€” no surprise there.

☁️ Amazon Linux 2023 β€” What's Different

Amazon Linux takes a completely different approach. It ships a 25 GB image with only 7% used:

[sysadmin@localhost ~]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sr0 11:0 1 364K 0 rom 
vda 252:0 0 25G 0 disk 
β”œβ”€vda1 252:1 0 25G 0 part /
β”œβ”€vda127 259:0 0 1M 0 part 
└─vda128 259:1 0 10M 0 part /boot/efi

Three partitions β€” a single massive 25 GB root partition, a 1 MB BIOS boot partition, and a tiny 10 MB EFI partition. No separate /boot. The filesystem is xfs, same as SLES.

[sysadmin@localhost ~]$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/vda1 xfs 25G 1.7G 24G 7% /
/dev/vda128 vfat 10M 1.3M 8.7M 13% /boot/efi

24 GB of free space out of the box. This image is built for production use, not minimal testing. You can install packages, run services, and never worry about disk space.

Memory usage is similar β€” 964 MB total, 317 MB used:

[sysadmin@localhost ~]$ free -m
 total used free shared buff/cache available
Mem: 964 317 433 2 213 508
Swap: 0 0 0

Cloud-init version:

[sysadmin@localhost ~]$ cloud-init --version
/usr/bin/cloud-init 22.2.2

One oddity: the hostname shows as localhost instead of kvmpodman. Amazon Linux ships with cloud-init 22.2.2, which is significantly older than Ubuntu's 25.3 or SLES's 25.1. The preserve_hostname: false directive may not be honored properly in this older version, or Amazon's own hostname logic overrides it during boot. Fix it manually:

[sysadmin@localhost ~]$ sudo hostnamectl set-hostname kvmpodman

Boot time:

real 0m19.998s
user 0m0.061s
sys 0m0.089s

About 20 seconds β€” the slowest of the three. Ubuntu and SLES both hit ~10 seconds. Amazon's extra startup time likely comes from its larger disk image, older cloud-init version, and the init services it brings up by default. Still, 20 seconds is nothing for a full production-grade Linux install.

The real kicker: this is an image designed exclusively for AWS EC2, and it boots natively as a KVM VM inside a Podman container. No AWS APIs, no special tooling β€” just qcow2 and VirtIO. It works.

πŸ” Distro Comparison

Metric Ubuntu 24.04 SLES 16 Amazon Linux 2023
Disk size 3.5 GB 1.3 GB 25 GB
Root usage 72% 97% 7%
Filesystem ext4 xfs xfs
Boot time ~10s ~10s ~20s
cloud-init 25.3 25.1 22.2
Partitions 4 3 3
Swap None None None

⚠️ Distro-Specific Gotchas

SLES:

  • Uses zypper instead of apt/dnf
  • Root filesystem is xfs, not ext4
  • Disk is extremely tight (97% used out of the box) β€” not ideal for installing additional packages
  • The wheel group works for sudo access
  • Older images may have cloud-init issues β€” grab the latest from the SUSE download center

Amazon Linux 2023:

  • Uses dnf like RHEL/CentOS
  • Comes with cloud-init pre-installed (it's designed for AWS)
  • May try to reach AWS metadata services on boot β€” harmless but adds a few seconds
  • Default shell for root is bash, but the ec2-user default varies

βœ… What You've Built

  • βœ… Ubuntu cloud image downloaded and ready to boot
  • βœ… cloud-init seed with user, SSH key, and hashed password
  • βœ… VM boots in under 10 seconds with VirtIO, fully configured
  • βœ… No manual installer, no interactive prompts
  • βœ… SLES 16 and Amazon Linux 2023 running side by side

πŸ”œ What's Next?

You can boot into the VM, but you're stuck in the console. Typing commands in the QEMU terminal is clunky β€” no copy/paste, no multiple windows, no real terminal features.

Post #5: We'll set up SSH networking so you can connect from your host terminal, use your favorite SSH client, and treat this VM like a real remote server.


This guide is Part 4 of the KVM Virtual Machines on Podman series.

Part 1: Build a KVM-Ready Container Image from Scratch
Part 2: KVM Acceleration in a Rootless Podman Container
Part 3: Persistent VMs in Podman: Install Alpine to a qcow2 Disk Image
Coming up in Part 5: SSH Into Your Podman VM β€” Container Networking for KVM


Found this helpful?