How to install Ubuntu Precise to a KVM guest on a headless server

This blog post explains how to install Ubuntu Precise (or other Ubuntu versions) to a virtualized KVM guest on a headless Linux server, i.e. on a server without a display connected or graphics (X11) software installed. We assume that the server host is Ubuntu or Debian. With some modifications the instructions can be made work on other Linux systems as well. The step-by-step instructions below contain explanations and troubleshooting tips as well.

The installation of the Ubuntu system in the KVM guest will be carried out over a VNC remote desktop connection. So in addition to the headless server host, you'll need a client desktop computer with a Linux GUI (X11) already installed. The default installation of any recent Linux distribution is fine. You may also use a client desktop running Mac OS X or Windows, but specific instructions for that are not provided in this howto. Also it is possible to install a headless Ubuntu guest automatically, without a GUI installer, but this topic is not covered in this howto.

The $ sign in front of the commands below indicates that these should be run as a regular user (not root) on the server. Please omit the $ sign when copy-pasting the commands. The -$ sign indicates that the command should be run on the client desktop (without the sign itself).

Autodetecting and installing KVM

If you want to check if KVM is supported on the host server before installing anything, follow these instructions. In a nutshell,

$ grep -Ec '(vmx|svm)' /proc/cpuinfo


$ grep -c hvm /sys/hypervisor/properties/capabilities

should print a number larger than 0 on the server host.

Install KVM on the server host:

$ sudo apt-get install qemu-kvm cpu-checker virtinst

Add yourself to the kvm and libvirtd groups:

$ sudo adduser "$USER" kvm
$ sudo adduser "$USER" libvirtd

Check that you are indeed a group member (both numbers must be larger than 0):

$ id | grep -c '(kvm)'
$ id | grep -c (libvirtd)'

If you are not, then login again, and check again.

Check whether KVM is supported on the server host:

$ /usr/sbin/kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used

Preparations on the server host

Download the Ubuntu .iso installer image from the Ubuntu alternative downloads page. Use the 32-bit download if your virtual machine has at most 2 GB of RAM. Even with more RAM, you may want to use the 32-bit, because it uses less memory. For me download filename was ubuntu-12.04.3-desktop-i386.iso and the size was 707 MB. (You may consider using a non-desktop version of Ubuntu.)

Move the downloaded .iso to /var/tmp (or any other world-readable directory).

Make sure that the .iso installer image is world-readable:

$ sudo chmod a+r /var/tmp/ubuntu-12.04.3-desktop-i386.iso

Create a sparse disk image file for the guest: (10 GB in the example)

$ rm -f /var/tmp/precise0.img
$ dd if=/dev/null bs=1G seek=10 of=/var/tmp/precise0.img

Please note that the virt-install command below can also create the disk image (if its --disk flag value is modified), making the dd step above unnecessary. But I think it's more didactic and safer to keep these steps separated.

Come up with a password to be used for the incoming GUI VNC remote desktop connections. The example command uses IncomingVpcShinyApe below, but for security please invent your own password and use that instead.

Creating the guest and starting the installation

Start the installation on the server host with the command below. No need do run as root, and no need to run in screen, because it exits in less than half a minute. The 2048 stands for 2GB of RAM.

DISPLAY= virt-install --connect qemu:///system -n precise0 -r 2048 --os-type=linux --os-variant=ubuntuprecise --disk /var/tmp/precise0.img,device=disk,bus=virtio,format=raw --network user --cdrom /var/tmp/ubuntu-12.04.3-desktop-i386.iso --noautoconsole --graphics vnc,port=5965,listen=,password=IncomingVncShinyApe

Starting install...
Creating domain...
Domain installation still in progress. You can reconnect to 
the console to complete the installation process.

If virt-install exits with an error message ERROR Guest name 'precise0' is already in use., then it may be a stale leftover from the previous runs. Remove it with the following command, and try virt-install again:

# Only if virt-install has exited with: already in use.
$ virsh -c qemu:///system undefine precise0

If virt-install exits with an error message such as ERROR Unable to read from monitor: Connection reset by peer, then take a look at the more specific error message here:

sudo less /var/log/libvirt/qemu/precise0.log

Specifying qemu:///system above and below is essential, because there is also qemu:///session, and various tools (such as virt-install and virsh) have different defaults.

Specifying --noautoconsole above makes sense (but it is not essential) on a headless server, because otherwise virt-install would attempt to start a VNC client, but that wouldn't work on the server without a GUI.

Don't worry if CPU usage on the server host jumps up to 100%. That's because the Unbuntu installer is booting from the installer CD .iso image in the KVM guest. It will go back to near 0% in a minute or so.

Each KVM guest has an XML configuration file. To see what virt-install has created, run this:

$ virsh -c qemu:///system dumpxml precise0

Preparations for the VNC connection

Check that the KVM guest is running and it's accepting incoming VNC connections:

$ ps $(sudo fuser -n tcp 5965 || echo -V) | cat

If you see procps version instead of a long (> 12 line) kvm command-line, then it's not running. Look at the log file above or the error messages printed by the previous commands to figure out what went wrong.

Please note that in the command-line printed by ps above, -vnc,password is normal. The actual password is not shown, and 65 is a correct port number, because VNC sometimes subtracts its base port number 5900. The server is actually listening on TCP port 5965.

On the server host, run the following command to check if the VNC connection can be made:

$ perl -we 'use IO::Socket::INET; alarm 3; my $s;
    die "error: $!\n" if !(
    my $buf; die "error: $!\n" if !sysread($s, $buf, 128);
    print $buf'
RFB 003.008

If it doesn't print a line starting with RFB, then it's a problem. Most probably there is a restrictive firewall active on the server host. Run this to fix it in a non-persistent way, and try the check again:

$ sudo iptables -I INPUT -p tcp --dport 5965 -i lo -j ACCEPT

The next big step is to use a VNC client to connect to the KVM guest, and complete the installation using the remote GUI. There are two was to connect:

  • An SSH tunnel (recommended), which is a little bit slower, but works over an SSH connection (port 22 by default).
  • A direct TCP connection, which is fast, but needs an end-to-end firewall configuration which lets the port 5965 go through. (Any port number at least 5900 is configurable.)

Start the SSH tunnel by running this from your client GUI desktop (substituting SERVERHOSTNAME with the host name of the server host running the KVM guest):

-$ ssh -L 5965: SERVERHOSTNAME

Keep this SSH connection open while using the VNC GUI below.

If you want a direct TCP connection instead, then enable incoming TCP connections on port 5965 on the server host. A simple, nonpersistent way of doing so is running:

$ sudo iptables -I INPUT -p tcp --dport 5965 -j ACCEPT

Now it's time to test that your client desktop computer can connect to the KVM guest using VNC. Run the following command (not the same as the similar one above) on the client desktop, replacing XSERVERHOSTNAME with if you are using the SSH tunnel, and with the server host name if you are using a direct TCP connection (works on Linux and Mac OS X):

$ perl -we 'use IO::Socket::INET; alarm 20; my $s;
    die "error: $!\n" if !(
    my $buf; die "error: $!\n" if !sysread($s, $buf, 128);
    print $buf'
RFB 003.008

If it emits something starting with RFB, then it's OK, otherwise it's a network or firewall problem which you have to diagnose and fix.

Connecting using VNC and clicking through the graphical installer

On the client desktop Linux system you can use any of the following commands as a VNC remote desktop client: vinagre, gvncviewer, xtightvncviewer, xvnc4viewer. The name of the package containing them is the same. (Pick just any VNC client if you use Mac OS X or Wondows) The recommaned command is vinagre, if it's available. Install vinagre to the client desktop with:

-$ sudo apt-get install vinagre

Start your VNC client, and connect it to the host depicted by XSERVERHOSTNAME (see above), port number 65. You can connect from the GUI or start the VNC client by specifying the target host and port in the command line, for example (don't forget to replace XERVERHOSTNAME as above):


The VNC client will ask for a password. Type the same password you have specified in the virt-install command-line.

With a bit of luck, you'll now be seeing the Ubuntu installer welcome screen (language selection, Try Ubuntu button and Install Ubuntu button). Click through the installation as usual.

If your VNC or SSH connection gets broken, don't worry, this doesn't abort the installation on the KVM guest. Just restart the connections, and continue clicking in VNC.

Once the installation has finished and the system has rebooted to the installed Linux, you can remove the installer image:

-$ rm -f /var/tmp/ubuntu-12.04.3-desktop-i386.iso

The --network user is a zero-configuration setting which works out-of-the-box, but it's a slow for production use. One the system is set up, you may want to change it in your /etc/libvirt/qemu/precise0.xml (using virsh) to bridged networking for much higher performance.

When you don't need VNC anymore, you may want to remove the firewall rule on the server host:

$ sudo iptables -R INPUT -p tcp --dport 5965 -j ACCEPT

1 comment:

Roshan said...

I tend to use debootstrap to populate the VM disk-image prior to booting the VM, and then setting up the rest of the files like /etc/hosts and fstab. Once the image is configured to my liking, I'll do a first-boot into the new image, and it's ready for use at this point. Creating a new VM and booting into it for the first time takes me less than 2 minutes as a result.