Bringing Your VMs to Kubernetes With KubeVirt

This article is dedicated to the open source project, which allows you to bring your virtual machine workloads to Kubernetes. A second part will explain how to use it with Kubermatic Kubernetes Platform.

What Is KubeVirt?

KubeVirt allows your virtual machine workloads to be run as pods inside a Kubernetes cluster. This allows you to manage them with Kubernetes without having to convert them to containers.

Why Do We Need KubeVirt?

Do you have a project running in VMs which you don’t want to convert to containers? Perhaps because it’s a legacy project for which you don’t want to go through the effort and expense of converting, but you’d like to orchestrate them with Kubernetes anyway? 

Do you have a project for which you require the special features of VMs?

In these cases, KubeVirt is the answer for you!

Trying It Out

This tutorial will guide you through the installation of KubeVirt with Minikube. Minikube allows you to run Kubernetes on your local computer. A second part will show you how to use KubeVirt with Kubermatic Kubernetes Platform.

Install minikube

First, you have to follow the installation instructions to install minikube. Then start it with:

minikube start

You should see this output:

😄  minikube v1.17.1 on Darwin 11.2.3
✨  Using the docker driver based on user configuration
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
💾  Downloading Kubernetes v1.20.2 preload ...
    > preloaded-images-k8s-v8-v1....: 491.22 MiB / 491.22 MiB  100.00% 3.84 MiB
🔥  Creating docker container (CPUs=2, Memory=1989MB) ...
🐳  Preparing Kubernetes v1.20.2 on Docker 20.10.2 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up the control plane ...
    ▪ Configuring RBAC rules ...
🔎  Verifying Kubernetes components...
🌟  Enabled addons: storage-provisioner, default-storageclass
    ▪ Want kubectl v1.20.2? Try 'minikube kubectl -- get pods -A'
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

You can use kubectl as usual in minikube, but you have to preface any command with minikube and put a – after kubectl. Example: the regular kubectl get pods becomes minikube kubectl -- get pods.

Enable Addon

Now you have to enable the KubeVirt addon:

minikube addons enable kubevirt
- Using image bitnami/kubectl:1.17
* The 'kubevirt' addon is enabled

You can now use KubeVirt! Check if the resources have been created correctly:

minikube kubectl -- get all -n kubevirt

You should see 7 pods, 3 services, 1 daemonset, 3 deployment apps, and 3 replica sets. It might take some time for all the objects to be ready and running.

NAME                                  READY   STATUS    RESTARTS   AGE
pod/virt-api-6bb566bbc7-crspt         1/1     Running      0       28m
pod/virt-api-6bb566bbc7-fh5jg         1/1     Running      0       28m
pod/virt-controller-5d6975c644-j4mbp  1/1     Running      2       28m
pod/virt-controller-5d6975c644-r6w7r  1/1     Running      3       28m
pod/virt-handler-brxxs                1/1     Running      2       28m
pod/virt-operator-976969b94-hsrpb     1/1     Running      2       30m
pod/virt-operator-976969b94-k78wh     1/1     Running      2       30m

NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubevirt-operator-webhook    ClusterIP   <none>        443/TCP   28m
service/kubevirt-prometheus-metrics  ClusterIP   <none>        443/TCP   28m
service/virt-api                     ClusterIP   <none>        443/TCP   28m

NAME  	                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE  
daemonset.apps/virt-handler    1         1         1        1             1           
NODE                         AGE       28m

NAME                                  READY    UP-TO-DATE   AVAILABLE   AGE
deployment.apps/virt-api               2/2         2            2       28m
deployment.apps/virt-controller        2/2         2            2       28m
deployment.apps/virt-operator          2/2         2            2       30m

NAME                                        DESIRED   CURRENT   READY    AGE
replicaset.apps/virt-api-6bb566bbc7          2          2         2      28m
replicaset.apps/virt-controller-5d6975c644   2          2         2      28m
replicaset.apps/virt-operator-976969b94      2          2         2      30m

NAME                           	            AGE       PHASE   	    30m       Deployed

Install virtctl

Run the following to install virtctl, a tool to explore graphical ports of the VM:

VERSION=$(kubectl get -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
echo ${ARCH}
curl -L -o virtctl${VERSION}/virtctl-${VERSION}-${ARCH}
chmod +x virtctl
sudo install virtctl /usr/local/bin

Create a VM Resource

Now create a file locally named vm.yaml, then copy and paste the below configuration into the file. Use minikube kubectl create -f vm.yaml to create the VM resource on the cluster:

kind: VirtualMachine
  name: testvm
  running: false
      labels: small testvm
            - name: containerdisk
                bus: virtio
            - name: cloudinitdisk
                bus: virtio
          - name: default
            bridge: {}
            memory: 64M
      - name: default
        pod: {}
        - name: containerdisk
        - name: cloudinitdisk
            userDataBase64: SGkuXG4=

Alternatively, you can download the manifest directly from Github using:


Then apply it to the cluster using the command:

minikube kubectl -- apply -f "testvm" created "small" created

Use kubectl get to check the VM status:

$ kubectl get vms

testvm   12m

You can get more information about the created “testvm” using:

$ kubectl get vms -o yaml testvm

Starting a Virtual Machine

You can start a virtual machine by using virtctl:

./virtctl start testvm

Alternatively, you can use kubectl patch:


Check if the virtual machines are running:

minikube kubectl -- get vms

testvm       5s          false

Peradventure the “Running” column is missing, you can use kubectl describe to check if the VM is running. The output will look like this:

$ kubectl describe vm
  Running:  true
      Creation Timestamp:  <nil>
      Labels:  testvm    small

Now that the vm is running, you can check the status. The status could come in three phases which are:

$ kubectl get vmis

NAME       AGE    PHASE          IP         NODENAME
testvm     50s    Scheduling
$ kubectl get vmis

NAME      AGE     PHASE          IP         NODENAME
testvm    3m43s   Scheduled                 minikube
$ kubectl get vmis

NAME      AGE     PHASE          IP         NODENAME
testvm    14m     Running    minikube

Scheduling: This is the beginning of the phases where the VM components are provisioned. 

Scheduled: The status changes to “scheduled” after a few minutes which also signals that the node is ready by outputting the node name. 

Running: This is the final stage where all the components have been provisioned by outputting the IP address.

As seen above, you are able to access your virtual machines with kubectl just like you would be able to with pods.

Clean-Up: Shutdown and delete To stop your virtual machine run:

./virtctl stop testvm

VM testvm was scheduled to stop

To delete the VM entirely, execute: "testvm" deleted

Stop minikube after you are done with the tutorial: minikube stop

Delete minikube: minikube delete –all

  • Successfully deleted all profiles


This is the first part of this blog post. The next part will guide you through using KubeVirt with Kubermatic Kubernetes Platform.

Where to Learn More

Irina Lindt

Irina Lindt

Software Engineer