1. Download an Ubuntu server cloud qcow2 disk image.
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
2. Create cloud-init configuration files
Also, you can generate an SSH key pair and use it to login over SSH if you put the public key as I did below. cloud-init will automatically write it to ~/.ssh/authorized_keys
touch meta-data
touch network-data
touch vendor-data
cat << EOF > user-data
#cloud-config
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
lock_passwd: false
password: password
ssh_authorized_keys:
- <your public SSH key>
chpasswd:
list: |
ubuntu:password
expire: False
ssh_pwauth: True
EOF
3. Create an ISO disk image out of those files
The volume id should be cidata. Here, I enabled the Rock Ridge and Joliet ISO 9660 extensions.
xorriso -as mkisofs \
-output seed.img \
-volid cidata -rational-rock -joliet \
user-data meta-data network-config
4. Run the guest OS
Notice that the disk image that holds Ubuntu Server and the CD-ROM disk image are both "plugged in" to the machine being emulated. See the QEMU documentation for more help on writing the correct and fastest QEMU invocation for your platform and use.
qemu-system-x86_64 \
-m 2G \
-accel tcg \
-drive file=noble-server-cloudimg-amd64.img,if=virtio,format=qcow2 \
-drive file=seed.img,format=raw,media=cdrom \
-netdev user,id=net0 \
-device virtio-net-pci,netdev=net0,hostfwd=tcp::2222-:22 \ # you only need hostfwd if you're using ssh
-nographic
Now, in the guest OS:
ubuntu@ubuntu:~$ uname -a
Linux ubuntu 6.8.0-90-generic #91-Ubuntu SMP PREEMPT_DYNAMIC Tue Nov 18 14:14:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
In this case, the username is ubuntu, and the password is password. If you set up SSH, you can login without your password:
ssh -p 2222 -i private_key ubuntu@localhost
5. Optionally, mount the host file-system onto guest's file system
Make sure that QEMU is compiled with support for the 9p protocol. Add this to the QEMU invocation:
-virtfs local,path=/Users/varunnawathey,mount_tag=share0,security_model=passthroughsudo mount -t 9p share0 /mnt