Build, Install, and Use QEMU on Ubuntu

This post describes how you can build, install, and use QEMU on an Ubuntu machine. I basically refererenced The test has proceeded on an Ubuntu 18.04 server machine.

QEMU Build

sudo apt install libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev \
git clone git://
cd qemu
git checkout v4.2.0
mkdir -p $HOME/qemu.sandbox/bin
cd $HOME/qemu.sandbox/bin
../../qemu/configure --enable-debug --enable-gtk
time make -j143
./x86_64-softmmu/qemu-system-x86_64 -L pc-bios

Guest OS Install

Get an Ubuntu server install image:

$ wget

Make qcow2 format image file:

cd ..
$ ./bin/qemu-img create -f qcow2 qc2img 32G
Formatting 'qc2img', fmt=qcow2 size=34359738368 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
$ ls -alh
total 830M
drwxrwxr-x  3 sjpark sjpark 4.0K Jun  1 20:34 .
drwxr-xr-x 28 sjpark sjpark 4.0K Jun  1 20:15 ..
drwxrwxr-x 94 sjpark sjpark  12K Jun  1 20:19 bin
-rw-r--r--  1 sjpark sjpark 193K Jun  1 20:34 qc2img
-rw-rw-r--  1 sjpark sjpark 848M Feb 16 05:37 ubuntu-18.04.3-live-server-amd64.iso

And, boot QEMU VM with the install image:

$ sudo ./bin/x86_64-softmmu/qemu-system-x86_64 -m 8G -enable-kvm \
	-drive if=virtio,file=qc2img,cache=none \
	-cdrom ubuntu-18.04.3-live-server-amd64.iso

If your session is connected with X, above command opens QEMU GUI for the booted VM. The VM will be booted with the Ubuntu server install image, as same as when you boot a machine with an install media. As usual, proceed the install of the Ubuntu server on the VM. This post will use ssh to connect to the VM. Install openssh pacakge while the install process.

Now, you can boot your Ubuntu VM as below:

$ sudo ./bin/x86_64-softmmu/qemu-system-x86_64 -m 20G -smp 32 -enable-kvm \
	-drive if=virtio,file=qc2img,cache=none \
	-net user,hostfwd=tcp::2242-:22 -net nic -nographic

Because of the -nographic option, it will not give you GUI interface but shows Booting from Hard Disk... message only. But, the VM is booted well. You can ssh to the VM via the port, 2242.

$ ssh localhost -p 2242

Connect Console to Terminal

Let’s connect the VM’s console to the QEMU executing terminal. Modify the /etc/default/grub file’s GRUB_CMDLINE_LINUX_DEFAULT to have "console=ttyS0 earlyprintk=ttyS0 ftrace_dump_on_oops".

You may also want to show GRUB when boot. Comment out GRUB_TIMEOUT_STYLE=hidden, give none-zero value to GRUB_TIMEOUT=, and Uncomment GRUB_TERMINAL=console.

Finally, commit the change by:

$ sudo update-grub
$ sudo shutdown -h now

After this, if you start the VM again using the QEMU command, the terminal you executed the QEMU command will be connected with the console of the VM and will show boot log and login prompt.

Return to VM Monitor Console

If you want to go back to the VM monitor console, which you have seen before connecting the QEMU executed terminal to the VM’s console, press Ctrl+a c. If you want to go back to the guest console again, press Ctrl+a c enter.


Boot with Kernel in Host

You can boot the VM to use a kernel image in the file system of the host machine. If you want to keep your development environment on your host machine but use the QEMU as a test target device, this is useful. Give the path to the kernel image file via -kernel option and give the kernel parameter you want to use using -append option. For example:

$ sudo ../qemu/build/x86_64-softmmu/qemu-system-x86_64 -m 2048 -smp 2 \
	-enable-kvm -drive if=virtio,file=debian.img,cache=none \
	-net user,hostfwd=tcp::2242-:22 -net nic -nographic \
	-kernel /linux.out/arch/x86/boot/bzImage \
	-append “root=/dev/vda1 console=ttyS0 earlyprintk=ttyS0 debug ignore_loglevel ftrace_dump_on_oops”

For the root= in the kernel parameter, check which device file is mounted as the rootfs by the guest OS and use it.

Also, you cannot use modules built on the host in the VM, unless you send the module binary files into the VM. If you don’t want to do that, simply build the modules you need in static. For example, as this post uses the ssh, I statically built the ethernet driver.

Image Resize

The guest os disk might be full soon, if you configured it too small. You can enlarge the qcow2 image’s size as below:

$ qemu-img resize qc2img +160G
$ sudo apt install libguestfs-tools
$ cp qc2img qc2img.bak
$ sudo virt-resize --expand /dev/sda1 qc2img.bak qc2img
$ sudo virt-resize --expand /dev/sda1 qc2img.bak qc2img
[   0.0] Examining qc2img.bak
◓ 25% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒═════════════════════════════════════════════════════════════════════⟧ --:--
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00

Summary of changes:

/dev/sda1: This partition will be resized from 24.0G to 184.0G.  The
filesystem ext4 on /dev/sda1 will be expanded using the 'resize2fs' method.

/dev/sda2: This partition will be left alone.

[  49.9] Setting up initial partition table on qc2img
[  56.7] Copying /dev/sda1
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
[ 594.9] Copying /dev/sda2
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
[ 767.4] Expanding /dev/sda1 using the 'resize2fs' method

Resize operation completed with no errors.  Before deleting the old disk,
carefully check that the resized disk boots and works correctly.
$ ./bin/qemu-img info qc2img
image: qc2img
file format: qcow2
virtual size: 192G (206158430208 bytes)
disk size: 32G
cluster_size: 65536
Format specific information:
    compat: 1.1
    lazy refcounts: false
    refcount bits: 16
    corrupt: false
$ virt-filesystems --long -h --all -a qc2img
libguestfs: warning: current user is not a member of the KVM group (group ID 117). This user cannot access /dev/kvm, so libguestfs may run very slowly. It is recommended that you 'chmod 0666 /dev/kvm' or add the current user to the KVM group (you might need to log out and log in again).
Name       Type        VFS      Label  MBR  Size  Parent
/dev/sda1  filesystem  ext4     -      -    184G  -
/dev/sda2  filesystem  unknown  -      -    1.0K  -
/dev/sda5  filesystem  swap     -      -    8.0G  -
/dev/sda1  partition   -        -      83   184G  /dev/sda
/dev/sda2  partition   -        -      05   1.0K  /dev/sda
/dev/sda5  partition   -        -      82   8.0G  /dev/sda
/dev/sda   device      -        -      -    192G  -

After that, if you boot the guest VM again and check, you can use the enlarged space:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            9.7G     0  9.7G   0% /dev
tmpfs           2.0G  8.6M  2.0G   1% /run
/dev/vda1       181G   24G  149G  14% /
tmpfs           9.8G     0  9.8G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           9.8G     0  9.8G   0% /sys/fs/cgroup
tmpfs           2.0G     0  2.0G   0% /run/user/1000


QCOW2 Image Mount

You might want to access to the guest VM image directly from the host. It is available using qemu-nbd tool. For that, load the nbd kernel module and connect the qcow2 format VM image via qemu-nbd:

$ cd qemu.sandbox
$ sudo modprobe nbd max_part=8
$ sudo ./bin/qemu-nbd --connect=/dev/nbd0 ./qc2img.bak

Now you can see the partitions in the image using fdisk as below:

$ sudo fdisk -l /dev/nbd0
Disk /dev/nbd0: 192 GiB, 206158430208 bytes, 402653184 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xe513ef69

Device      Boot     Start       End   Sectors  Size Id Type
/dev/nbd0p1 *         2048 385877631 385875584  184G 83 Linux
/dev/nbd0p2      385877632 402650753  16773122    8G  5 Extended
/dev/nbd0p5      385877634 402650753  16773120    8G 82 Linux swap / Solaris

Mount a partition you want to access:

$ sudo mount /dev/nbd0p1 ./mnt/
$ cd mnt/
$ ls
bin   etc         initrd.img.old  lib64       media  proc  sbin  sys  var
boot  home        lib             libx32      mnt    root  snap  tmp  vmlinuz
dev   initrd.img  lib32           lost+found  opt    run   srv   usr  vmlinuz.old

If you are done, unmount and disconnect the device using qemu-nbd.

$ sudo umount ./mnt
$ sudo ./bin/qemu-nbd --disconnect /dev/nbd
nbd0    nbd0p2  nbd1    nbd11   nbd13   nbd15   nbd3    nbd5    nbd7    nbd9
nbd0p1  nbd0p5  nbd10   nbd12   nbd14   nbd2    nbd4    nbd6    nbd8
$ sudo ./bin/qemu-nbd --disconnect /dev/nbd0
/dev/nbd0 disconnected
SeongJae Park
Kernel Development Engineer

SeongJae Park is a programmer who loves to analyze and develop systems.