What if you could manage traditional Virtual Machines and modern containerized applications on the same Kubernetes platform?
When we talk about Kubernetes, the first thing that usually comes to mind is containers, Pods, Deployments, and Services.
But infrastructure is not always that simple.
Many organizations still depend on traditional Virtual Machines. Some applications cannot easily be containerized, while others rely on existing virtualized environments that are already working well.
So, does adopting Kubernetes mean leaving all those virtual machines behind?
Not at all.
This is where KubeVirt becomes interesting.
In this article, we'll understand why KubeVirt exists, what we can do with it, and most importantly, how to create, start, access, stop, and delete a virtual machine using a practical Kubernetes lab.
Let's explore!
1. What is KubeVirt?
KubeVirt is an open-source technology that extends Kubernetes to support virtual machine workloads.
Its main idea is simple: allow development teams to manage both traditional virtual machines and containerized applications within a common Kubernetes environment.
Instead of treating virtual machines and containers as two completely separate worlds, KubeVirt makes it possible to bring them together.
This is particularly useful for teams that have already adopted Kubernetes but still have applications running inside virtual machines.
- Why KubeVirt?
Let's understand the problem KubeVirt addresses.
Imagine a development team that has:
Existing applications running inside virtual machines.
New applications being developed using containers.
Some legacy applications that cannot easily be containerized.
A requirement to manage everything through a common platform.
Managing these workloads separately can introduce additional complexity.
KubeVirt provides a unified development platform where teams can build, modify, and deploy applications residing in both application containers and virtual machines.
One of the most interesting benefits is that teams don't have to migrate everything at once.
They can gradually containerize applications, break larger workloads into smaller services, and continue using existing virtualized components wherever necessary.
In my opinion, this gradual modernization approach is one of the most practical reasons to explore KubeVirt.
- What Can We Do with KubeVirt?
There are three important capabilities worth understanding.
- KubeVirt and Kubernetes
KubeVirt allows teams to use Kubernetes to manage virtual machines for applications that are impractical to containerize.
This brings VM workloads into the Kubernetes environment.
- KubeVirt and Virtual Machines
You can combine existing virtualized workloads with newly developed container workloads on a single platform.
This makes it easier to work with both types of applications without immediately replacing existing infrastructure.
- Containerized Applications
KubeVirt also supports the development of new microservice applications in containers that interact with existing virtualized applications.
This is particularly useful when an organization is modernizing its application architecture step by step.
- Let's Get Hands-On with KubeVirt!
Enough theory. Let's work with an actual virtual machine.
For this practical exercise, we'll use the KubeVirt interactive lab available on Killercoda.
Lab environment: KubeVirt 101 – Killercoda
You can experiment with the commands directly in your browser without setting up your own Kubernetes environment.
Step 1: Create a Virtual Machine
First, download the sample VM manifest provided by KubeVirt.
Run:
wget https://kubevirt.io/labs/manifests/vm.yaml
Now, explore the downloaded YAML file:
less vm.yaml
Take a moment to understand the manifest. It defines the configuration required to create the virtual machine.
One important detail: this example uses a ContainerDisk.
A ContainerDisk allows a container image to be used as a virtual machine disk.
However, the example disk does not persist data. Any changes made inside this VM should not be treated as permanent storage.
The provided example uses a Cirros virtual machine.
Step 2: Apply the VM Manifest
Now let's create our first virtual machine.
Run:
kubectl apply -f https://kubevirt.io/labs/manifests/vm.yaml
You should receive an output similar to:
virtualmachine.kubevirt.io *"testvm" * created
Congratulations! Your VirtualMachine resource has been created.
But there is something important to understand here.
Creating a VirtualMachine resource does not necessarily mean that its virtual machine instance is already running.
Let's explore that difference.
Step 3: List Existing Virtual Machines
To see the available virtual machines, execute:
kubectl get vms
You can also inspect the complete YAML configuration:
kubectl get vms -o yaml testvm
This helps you understand the current configuration and state of the VM.
Step 4: Start the Virtual Machine
Now it's time to start our VM.
Use the following command:
virtctl start testvm
Here, virtctl is the command-line tool used to interact with KubeVirt resources.
If you have installed virtctl through Krew, you can also use the Kubernetes plugin command:
**
kubectl virt start testvm**
After executing the command, check the VM status:
kubectl get vms
You should now see the VM in the Running state.
That's our virtual machine running through KubeVirt!
Step 5: Understand VM and VMI
This is one of the most important concepts in KubeVirt.
You might have noticed two different resource names:
VM – VirtualMachine
VMI – VirtualMachineInstance
They are related, but they are not the same thing.
VirtualMachine (VM) represents the virtual machine configuration and its desired lifecycle.
VirtualMachineInstance (VMI) represents an actual running instance of that virtual machine.
Let's check the running instances:
kubectl get vmis
To inspect the complete VMI configuration:
kubectl get vmis -o yaml testvm
Here is the interesting part:
Before starting the VM, its VMI does not exist.
When you start the VM, KubeVirt creates a VMI to represent the running instance.
When you stop the VM, that VMI is deleted.
The VirtualMachine resource can still remain available.
This distinction is important when working with VM lifecycle management.
Another thing to remember: changing the VM configuration does not automatically update an already-running VMI. If you modify properties that require a new instance, the existing VMI needs to be replaced by restarting the VM.
Step 6: Access the Virtual Machine Using Serial Console
Now that our VM is running, let's access it.
Execute:
virtctl console testvm
This connects you to the virtual machine's serial console.
For the Cirros VM:
Press Enter a few times.
Wait for the login prompt.
Use the username and password displayed by the lab.
You should now be inside the virtual machine.
You can interact with the guest operating system through this console.
To disconnect from the serial console, press:
Ctrl + ]
A useful tip: If you want to observe the complete boot sequence through the serial console, connect immediately after starting the VM. You can test this by stopping and starting it again.
Step 7: Stop the Virtual Machine
Once you have finished experimenting, you can shut down the virtual machine.
Run:
virtctl stop testvm
Step 8: Delete the Virtual Machine
Finally, let's clean up our lab environment.
To delete the virtual machine, execute:
kubectl delete vm testvm
This removes the VirtualMachine resource.
And that's it!
You've successfully explored the basic KubeVirt workflow, from creating a VM to accessing and managing its lifecycle.
- What Did We Learn?
Let's quickly recap what we covered in this practical exercise.
Task
Command
Download VM manifest
wget https://kubevirt.io/labs/manifests/vm.yaml
Create VM
kubectl apply -f vm.yaml
List VMs
kubectl get vms
Start VM
virtctl start testvm
List VM instances
kubectl get vmis
Access VM
virtctl console testvm
Stop VM
virtctl stop testvm
Delete VM
kubectl delete vm testvm
The most important concepts from this lab are:
KubeVirt brings virtual machine workloads into Kubernetes.
A VM and a VMI represent different things.
Starting a VM creates its running instance.
The serial console provides access to the guest operating system.
VM lifecycle operations can be performed using virtctl and Kubernetes commands.
- Final Thoughts
What I find interesting about KubeVirt is its approach to infrastructure modernization.
We often hear that containers are the future and that applications should move away from traditional virtual machines.
But real infrastructure doesn't change overnight.
Organizations have existing workloads, applications, dependencies, and systems that cannot always be migrated immediately.
KubeVirt offers an interesting way to bring these workloads into the Kubernetes ecosystem while allowing teams to adopt containerization gradually.
If you're learning Kubernetes, DevOps, or cloud infrastructure, I would definitely recommend exploring this project.
Start with a simple virtual machine, understand the difference between VM and VMI, experiment with lifecycle operations, and gradually explore more advanced features.
Sometimes, learning a new technology becomes much easier when you stop reading about it and actually start experimenting.
Have you tried KubeVirt yet? I'd love to hear about your experience in the comments!


