How to run virtual machines without going through the manual installation process.


  • Get a cloud image to use as master qcow2 image
  • Create an overlay image
  • Create a cloud-init config image
  • Run a vm

Step by step

Get a cloud image

Cloud images are similar to Live-images, i.e. they contain already installed system. But additionally they contain a cloud-init package. This package adds a cloud-init-local and cloud-init services which start on boot and reconfigure system according to the provided cloud-init configuration.

You can search for cloud images directly or check the collection of links in OpenStack documentation.

For CentOS 7 the latest cloud image is available at CentOS site

Download it:

$ wget -O centos-7-cloud.qcow2

Create an overlay image

Unlike the installation disks (ISO images), which are mounted to the system in a read-only mode, QCOW2 images are used by the running system. Qcow2 image is a "hard drive" of a virtual machine and every virtual machine needs its own image to run.

One approach would be to copy the base image every time you need a new vm. But since QCOW2 supports a copy-on-write approach (it is a Qemu Copy On Write format after all), we can create layered images instead.

We create example.qcow2 storage file backed by the master image ./master/centos-7-cloud.qcow2

$ qemu-img create -f qcow2 -o \
    backing_file=./master/centos-7-cloud.qcow2 \

The example.qcow2 is now an overlay and contains only changes from the base image. As we haven't changed anything yet, it is pretty small:

example.qcow2        193K 
centos-7-cloud.qcow2 833M

Create cloud config

Now here is the most complicated part, as apparently there is no documentation or specification on the cloud-init format, only some conventional knowledge.

For Centos 7 the more or less trusted source of documentation is RHEL Atomic Host Guide.

To provide configuration for cloud image we create an additional read-only image with configuration data and mount it into vm on boot as a CD-drive.

Minimal set of configuration data:

  • file meta-data:

    instance-id: example
  • file user-data:

      - ssh-rsa ...

To create an ISO:

$ genisoimage -o example_config.iso -V cidata -r -J user-data meta-data

Now we are ready to boot the VM.

Run VM

We have a storage image example.qcow2 and config image example_config.iso.

To run a VM:

$ virt-install --connect qemu:///system \
         -n example \
         -r 512 -w network=default \
         --import \
         --disk path=./example.qcow2 \
         --disk path=./example_config.iso,device=cdrom \

--nographics option forces virt-install to redirect the console output to the terminal window. After successful boot you get to the vm promt:

[  OK  ] Started Execute cloud user/final scripts.
[  OK  ] Reached target Multi-User System.
         Starting Update UTMP about System Runlevel Changes...
[  OK  ] Started Update UTMP about System Runlevel Changes.

CentOS Linux 7 (Core)
Kernel 3.10.0-693.17.1.el7.x86_64 on an x86_64

localhost login: 

To exit this mode use Ctrl-].

If all gone well, somewhere in that boot log you can find the network configuration options:

ci-info: ++++++++++++++++++++++++++++++++Net device info++++++++++++++++++++++++++++++++
ci-info: +--------+------+-----------------+---------------+-------+-------------------+
ci-info: | Device |  Up  |     Address     |      Mask     | Scope |     Hw-Address    |
ci-info: +--------+------+-----------------+---------------+-------+-------------------+
ci-info: |  lo:   | True |    |   |   .   |         .         |
ci-info: |  lo:   | True |        .        |       .       |   d   |         .         |
ci-info: | ens2:  | True | | |   .   | 52:54:00:82:83:f6 |
ci-info: | ens2:  | True |        .        |       .       |   d   | 52:54:00:82:83:f6 |
ci-info: +--------+------+-----------------+---------------+-------+-------------------+
ci-info: +++++++++++++++++++++++++++++++Route IPv4 info+++++++++++++++++++++++++++++++
ci-info: +-------+---------------+---------------+---------------+-----------+-------+
ci-info: | Route |  Destination  |    Gateway    |    Genmask    | Interface | Flags |
ci-info: +-------+---------------+---------------+---------------+-----------+-------+
ci-info: |   0   |    | |    |    ens2   |   UG  |
ci-info: |   1   | |    | |    ens2   |   U   |
ci-info: +-------+---------------+---------------+---------------+-----------+-------+

Then to access the vm use ssh centos@


While this is a nice way to test cloud images, and one can automate and extend it, let's face it: to set up a local vm lab you better use Vagrant with libvirt driver and sshfs plugin see the example.