Automated KVM Installs

Problem: Installing VMs for testing, lab environments, or projects is tedious and boring.
Solution: Automate the installation process with KVM and Kickstart.

Hello L1T!

This is my attempt at writing a wiki to assist in exposing you to automation, automation, beautiful automation. Rather than jump right into OpenStack, Docker, Kubernetes, and making your very own Stark Industries… I figured we would start with something a little bit simpler.
For this wiki, I am going to be using Debian Stretch “””server edition”””, with Kernel 4.9.0-6. The same concepts apply for CentOS 7.4 1708 with Kernel 4.15.13-1, but there are subtle differences. If you want to follow along exactly, I recommend using Debian. Otherwise, if you’re not afraid of some trial and error or doing your own research and tweaks, feel free to use any system you want! :smiley:

The first thing we’re going to do is setup our environment.

sudo apt install qemu-kvm libvirt-clients libvirt-daemon-system virt-viewer virt-manager -y
Add your user to the appropriate groups:
sudo adduser admindev libvirt
sudo adduser admindev libvirt-qemu

Obviously, replace admindev with your username.


Welcome back. I have built a few aliases to assist me with this process. In .bashrc (or, even better, make a .bash_aliases file that .bashrc references):
alias show=’virsh –connect qemu:///system list –all’
alias get-vm=’virsh –connect qemu:///system’

What do these do? show allows me to view the VMs in their current state. get-vm VMNAME start/shutdown/destroy/undefine allows me to easily control the state of a specific VM.

These aliases are not necessary, and definitely not worth it if you just need a VM or two.

Next, we’re going to install Apache for our image server.

sudo apt install apache2 -y
sudo systemctl start apache2 && sudo systemctl enable apache2

Verify that it works by visiting the IP of the server in your browser. You should get the message that Apache has passed the test. If you don’t receive that message, try disabling ufw or your firewall of choice. If the test works after that, you’ll have to create a rule to allow http and https traffic. That information is not within the scope of this guide (yet).

This is where things might get a little… controversial. I had an existing Kickstart configuration from another server. You can do this one of two ways: Copy the generic Kickstart file from github or various other sources and tweak to your specifications. Or, fire off a CentOS server and copy the anaconda-ks.cfg from the root directory and tweak that. Option 2 is a lot better, because the admin and user passwords will be hashed in that file and used in the new VM. Either way, I leave the decision up to you.

So, with your Kickstart file, hang tight, we’re not using that yet! We have to get our networking working and then pass the iso through to the image file.

We’re halfway there, though! (Woooaaahhhh! Livin on a – ahem).

For your VMs to communicate to the host and the Internet, we’re going to need to adjust our /etc/networking/interfaces file. I used vim,
you can use your editor of choice. I highly recommend vim, though, because it comes on EVERY server. One day you’re going to have to cowboy edit a line of code in production and you’re going to thank Wendell (PBUH) that you knew how to do a rapid-fire search using vim :wink:

Bridged Networking

The Debian docs do it a little something like this:

auto lo
iface lo inet loopback

# The primary network interface
auto eth0

#make sure we don't get addresses on our raw device
iface eth0 inet manual
iface eth0 inet6 manual

#set up bridge and give it a static ip
auto br0
iface br0 inet static
        bridge_ports eth0
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 0

#allow autoconf for ipv6
iface br0 inet6 auto
        accept_ra 1

If this makes you a little uncomfortable, fret not! I will let you know what I deleted in the original file:

I commented out the file, and saved it, then I added the appropriate contents above. This way, if the bridge didn’t work, or if something catastrophic happened, I wasn’t scrambling on the internet for the default config or, worse, completely reinstalling Debian.

Now, for address, netmask, network, broadcast, gateway, and dns-nameservers, you want to edit that to your environment. For the address, since this badboy was the central hub for all of my work, I made it the “first”: If you’re on a, you can do or, anything that you can easily remember AND something that’s not within the lease pool of addresses.

Subnet mask is /24, so I left as the netmask.

Network (base subnet) for my network is

Broadcast for my network:

Gateway and DNS-Nameserver is for me. I don’t need Google to do my routing.

Save the file and reboot. You should be able to ping out or ssh into your box if everything is correct. If not, definitely triple check the configuration and make sure you have the appropriate packages:

Copy the image to the image server

Create a temp folder
sudo mkdir /temp
Download CentOS iso. I used wget since I had no GUI
Extract iso contents to the temp folder you created.
sudo mount -o loop CentOS-7-x86_64-Minimal-1708.iso /temp
Create an install path in the Apache directory:
sudo mkdir /var/www/html/install
Copy iso contents over to the install path
sudo cp -a /temp/. /var/www/html/install

Now, you can visit the IP of your server in the browser followed my /install to view the contents of the iso. So, in my case:


Remember that Kickstart file? It’s pretty good on its own. I added some tweaks so it would reboot afterward. Here’s mine:

# System authorization information
auth --enableshadow --passalgo=sha512
firstboot --enable
ignoredisk --only-use=sda
# Keyboard layouts
keyboard --vckeymap=us --xlayouts='us'
# System language
lang en_US.UTF-8

# Network information
network --bootproto=static --device=ens3 --gateway= --ip= --nameserver= --netmask= --ipv6=auto --activate
network --hostname=testbench

# Root password
rootpw --iscrypted $PWHASH
# System services
services --disabled="chronyd"
# System timezone
timezone America/Chicago --isUtc --nontp
# Add user
user --groups=wheel --name=sysadmin --password=$PWHASH --iscrypted --gecos="sysadmin"

# Restart when install is complete

# System bootloader configuration
bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
# Partition clearing information
clearpart --all --initlabel --drives=sda
part swap --asprimary --fstype="swap" --size=4096
part /boot --fstype xfs --size=200
part pv.01 --size=1 --grow
volgroup rootvg01 pv.01
logvol / --fstype xfs --name=lv01 --vgname=rootvg01 --size=1 --grow
# Disk partitioning information
#part /boot/efi --fstype="efi" --ondisk=sda --size=512 --fsoptions="umask=0077,shortname=winnt"
#part swap --fstype="swap" --ondisk=sda --size=4096
#part /home --fstype="xfs" --ondisk=sda --size=1717247
#part / --fstype="xfs" --ondisk=sda --size=2092543

url --url



%addon com_redhat_kdump --enable --reserve-mb='auto'

The network information is crucial, you do not want to keep creating VMs that have the same IP information. Always edit that before you create a new VM.

The Partition Clearing information is not necessary. I added this for customization.

Otherwise, the default drive that is created will work. Here is an easier file:

# System authorization information
auth --enableshadow --passalgo=sha512
firstboot --enable
ignoredisk --only-use=sda
# Keyboard layouts
keyboard --vckeymap=us --xlayouts=''
# System language
lang en_US.UTF-8

# Network information
network  --bootproto=static --device=ens2 --gateway= --ip= --nameserver= --netmask=255.255.255 network  --hostname=mongo02
# Root password
rootpw --iscrypted $PWHASH
# System services
services --enabled="chronyd"
# Do not configure the X Window System
# System timezone
timezone America/Chicago --isUtc
user --groups=wheel --name=chris --password=$PWHASH

# Reboot when install is complete
# System bootloader configuration
bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
autopart --type=lvm
# Partition clearing information
clearpart --all --initlabel --drives=sda



%addon com_redhat_kdump --enable --reserve-mb='auto'


pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty

I hope you get the idea! It is fun to go through each line and see what it does. We’re almost there!

Copy Kickstart over to the install path.

sudo cp ks.cfg /var/www/html/install

Allow read permissions
sudo chmod +r /var/www/html/install/ks.cfg

Time to build the VMs

I have two scripts that I use for this. You are more than welcome to create the scripts or use the command line.

Script 1 is to create and allocate data for the VM. Be sure to put however much data you want and to change the name.img and name.qcow2 to what you want your VM name to be:

fallocate -l 40G name.img 
qemu-img create -f qcow2 ./name.qcow2 40G

You can also create directories for various VMs. Either way, be sure to create the HDD where you want the VMs to live. I do that anyway. You can also put them where QEMU recommends, which is /var/somewhere/qemu – But a lot of /var partitions aren’t that big. I prefer using my /home partition.

From here, you build out your VM using virt-install. Rather than using \ to new line everything, I just threw it all into a .sh file.

sudo virt-install --name testbench --ram 4096 --disk path=./testbench.qcow2,size=60 --vcpus 4 --os-type linux --os-variant generic --network bridge=br0 --graphics none --console pty,target_type=serial --location '' --extra-args 'console=ttyS0,115200n8 serial' -x ks=

Change all of the above to fit the specs of your VM preferences; Name, RAM, CPUs. The –extra-args=’console…’ aren’t necessary if you don’t want to watch the process. The VM will reboot. On my system, depending on the VM specs, it takes 2 to 5 minutes to complete the installation of a CentOS minimal.

That’s it! Shortly you will see the indication that it is working:


Feel free to make contributions for different operating systems or, if you can make something more efficient in the scripts or Kickstart file, please do! This setup has worked for me when doing development work and school work. Is nice to be able to make some brief edits and shoot off some scripts to have a new VM up and running in minutes.

Meta discussion of this article can be found here


One year later

Made a video on the script I made for provisioning infrastructure.

1 Like