Introduction
The Raspberry Pi has long transcended its original role as a simple board for enthusiasts: today it is a fully functional mini-computer capable of performing complex tasks, including hardware virtualization. This article provides a step-by-step guide to installing Raspberry Pi OS Lite, configuring SSH access, updating the system, installing KVM support, setting up the Cockpit web interface, and creating and running a guest operating system using KVM. This approach allows for transforming the Raspberry Pi into a convenient platform for testing and learning in the field of virtualization.
Installing Raspberry Pi OS Lite
Using the Raspberry Pi Imager utility, we install the Raspberry Pi OS Lite (64) operating system on a micro SD card. During the installation phase, we need to change the configuration so that we can then work with the Raspberry Pi remotely by connecting via SSH.
- Hostname
- Username and password
- Enable SSH


We boot the Raspberry Pi from this memory card and check if it is connected to the network, if not, then we initialize the process of configuring the network connection by calling the system configurator:
The Raspberry Pi connection must be via cable and not via wifi.
System Update
Connecting to the Raspberry Pi via SSH
ssh [email protected]
We update the OS to the latest version and restart the Raspberry Pi
sudo apt update && sudo apt full-upgrade -y
sudo reboot
Installing KVM + libvirt
Starting with the Linux kernel 5.10+ (and now Raspberry Pi OS uses 6.x), support for KVM hardware virtualization for ARM64 is included directly in the kernel. On Raspberry Pi OS (64-bit, desktop, or lite), KVM is activated automatically as soon as the device supports hardware virtualization.
This was made possible by the fact that the Raspberry Pi Foundation integrated KVM support directly into the upstream kernel for its ARM64 platforms. They focus on the fact that the user can immediately experiment with virtualization – without additional configuration.
However, it is better to check this by running the following commands:
if the file /dev/kvm exists – support is active
ls /dev/kvm
/dev/kvm
will show the KVM startup log
dmesg | grep -i kvm
[ 1.078559] kvm [1]: nv: 554 coarse grained trap handlers
[ 1.084177] kvm [1]: IPA Size Limit: 40 bits
[ 1.088486] kvm [1]: GICV region size/alignment is unsafe, using trapping (reduced performance)
[ 1.097272] kvm [1]: vgic interrupt IRQ9
[ 1.101231] kvm [1]: VHE mode initialized successfully
[ 3.526966] systemd[1]: Hostname set to <kvm>
Since kvm works by default, you need to install libvirt
sudo apt install libvirt-daemon-system libvirt-clients bridge-utils virtinst
Checking the operation of the service:
systemctl status libvirtd
● libvirtd.service - Virtualization daemon
Loaded: loaded (/lib/systemd/system/libvirtd.service; enabled; preset: enabled)
Active: active (running) since Thu 2025-05-08 10:42:46 BST; 25s ago
TriggeredBy: ● libvirtd-admin.socket
● libvirtd-ro.socket
● libvirtd.socket
Docs: man:libvirtd(8)
https://libvirt.org
Main PID: 4708 (libvirtd)
Tasks: 19 (limit: 32768)
CPU: 138ms
CGroup: /system.slice/libvirtd.service
└─4708 /usr/sbin/libvirtd --timeout 120
May 08 10:42:46 kvm systemd[1]: Starting libvirtd.service - Virtualization daemon...
May 08 10:42:46 kvm systemd[1]: Started libvirtd.service - Virtualization daemon.
Change user permissions
You need to add a user to the libvirt and kvm groups. Since we created the kvm user in the first step, there is no need to add it to the kvm group, but if you created another user in the initial step, you need to add it to the kvm group.
sudo usermod -aG libvirt kvm
After adding a user to the libvirt group, in order not to reboot the system, you can activate the new permissions with the command:
newgrp libvirt
Installing the Cockpit
Cockpit – це веб-інтерфейс для керування Linux-серверами, і один із його плагінів — Cockpit Machines — є надбудовою саме для KVM
sudo apt install cockpit cockpit-machines
Checking the operation of the service:
sudo systemctl status cockpit.socket
● cockpit.socket - Cockpit Web Service Socket
Loaded: loaded (/lib/systemd/system/cockpit.socket; enabled; preset: enabled)
Active: active (listening) since Thu 2025-05-08 10:59:18 BST; 46s ago
Triggers: ● cockpit.service
Docs: man:cockpit-ws(8)
Listen: [::]:9090 (Stream)
Tasks: 0 (limit: 9561)
CPU: 12ms
CGroup: /system.slice/cockpit.socket
May 08 10:59:18 kvm systemd[1]: Starting cockpit.socket - Cockpit Web Service Socket...
May 08 10:59:18 kvm systemd[1]: Listening on cockpit.socket - Cockpit Web Service Socket.
The service is active, but in listening mode, you need to activate it
sudo systemctl status cockpit.socket
● cockpit.socket - Cockpit Web Service Socket
Loaded: loaded (/lib/systemd/system/cockpit.socket; enabled; preset: enabled)
Active: active (running) since Thu 2025-05-08 10:59:18 BST; 3min 24s ago
Triggers: ● cockpit.service
Docs: man:cockpit-ws(8)
Listen: [::]:9090 (Stream)
Tasks: 0 (limit: 9561)
CPU: 12ms
CGroup: /system.slice/cockpit.socket
May 08 10:59:18 kvm systemd[1]: Starting cockpit.socket - Cockpit Web Service Socket...
May 08 10:59:18 kvm systemd[1]: Listening on cockpit.socket - Cockpit Web Service Socket.
Now you can follow the link to port 9090 in your browser: https://192.168.99.181:9090/

Guest OS Test
You need to log in through the Cockpit web interface using the username and password of the server user. The first thing the user will see is a dashboard with general information about the server

I previously downloaded the Ubuntu Server ISO image ubuntu-24.04.2-live-server-arm64.iso and placed it in the root of the system in the iso directory. I will run this image locally.
- Name: Ubuntu_server
- Connection: System
- Installation type: Local install medis (ISO image or distro install tree)
- Installation source: /iso/ubuntu-24.04.2-live-server-arm64.iso
- Operating system: Generic Linux 2022
- Storage: Create new volume
- Storage limit: 20 Gib
- Memory: 4 Gib

To start the build, click create and run, but after a moment you will get an error:
Creation of VM Ubuntu_server failed
ERROR Requested operation is not valid: network ‘default’ is not active Domain installation does not appear to have been successful. If it was, you can restart your domain by running: virsh –connect qemu:///system start Ubuntu_server otherwise, please restart your installation.
This error means that the virtual machine cannot be created because the default network is inactive. Libvirt creates a virtual network named default by default, but it may be inactive or not automatically started at startup. To fix this, you need to enable the network and add it to autostart:
sudo virsh net-start default
Network default started
sudo virsh net-autostart default
Network default marked as autostarted
Let’s check the status of the default network:
sudo virsh net-list --all
Name State Autostart Persistent
--------------------------------------------
default active yes yes
Let’s try to run the guest OS creation again. Click create and run again, and the image is mounted successfully, which indicates that the service is working correctly. The corresponding entry has appeared in the list of virtual machines.

To define and change the settings of a virtual OS, or to access OS management, at least for the logical completion of the installation, you must select the required OS from the list and then the user will be taken to the dashboard of this OS with its detailed characteristics.

To enlarge the console screen, click on the Expand button, and proceed to install Ubuntu Server for ARM. In my case, the window was partially cropped, so I installed the OS intuitively, selecting parameters with the left, right, tab, spacebar and enter buttons.
The first launch of the installed OS was not successful, a loop conflict occurred. In the console, selecting Serial Console, I saw many messages: Recursive exception occurred while dumping the CPU state.

After shutting down the virtual machine and after a certain startup time, the second attempt still launched, but not without errors. Since this OS is cloud-based, it needs access to the Internet. At the moment, the Internet is not configured, so the launch took place after waiting for the message:
Connected to domain 'linux2022-2025-5-10'
[ *** ] Job systemd-networkd-wait-online.se…tart running (1min 22s / no limit)
But the OS still started!

Now you need to go to the section for configuring the local network and Internet via the bridge.
Network settings
Warning! The following settings may cause a loss of connection to the host, so before executing any command, make sure you have physical access to the host so that you can later correct the settings and regain access.
I was never able to successfully complete the network settings, so if you know an easy way to implement this, please share your experience in the comments.
By default, the built-in virtio interface is used with the following network 192.168.122.1/24, the guest OS was assigned IP 192.168.122.39, but the host itself is in a different subnet, which requires some configuration.

The bridge is created in the Networking -> Interfaces section, select Add Bridge and specify the name bridge0, do not check any boxes, as this may lead to loss of connection with the host. Thus, a new interface bridge0 with automatic DHCP IP address acquisition has appeared in the list of interfaces. Currently, there is no interface associated with this bridge, so we continue the configuration on the host via the terminal.
We need to make sure that the physical interface of the host is included in the bridge
sudo brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.000000000000 no
virbr0 8000.5254006ad9b7 yes
In the output of this command, the interfaces column is empty, which indicates that the interfaces are not linked to each other, you need to link them manually with the following command:
sudo brctl addif bridge0 eth0
sudo ip link set bridge0 up
We run the bridge status check command again and see that the interface column has already acquired a connection to the physical interface:
sudo brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.d83adde83f07 no eth0
virbr0 8000.5254006ad9b7 yes
After that, the bridge will perform its function. Now we go back to the Cockpit web interface, to the Networking section, and expect the bridge to provide an IP address.

The IP address of the correct subnet appeared in the IP address column, but due to some peculiarities, the virtual machine never received it.
Conclusion
Using KVM virtualization and Cockpit on Raspberry Pi is a great opportunity to learn, test and experiment in the world of ARM64 virtualization. This combination allows users to gain a deeper understanding of the principles of hypervisor, resource management and network interface configuration.
However, practical application remains quite questionable due to several serious limitations:
- Compatibility of ARM64 operating systems
- Many popular OSes have .img instead of .iso, which makes them difficult to run in KVM.
- Not all distributions support UEFI correctly, which leads to difficulties with booting.
- Lack of official optimized ARM64 distributions in .iso format.
- Unstable operation of KVM on ARM64
- Frequent CPU compatibility problems (KVM is not supported for this guest CPU type).
- Critical errors such as “Recursive Exception occurred while dumping the CPU state” occur, making virtual machines unpredictable.
- Limited support for host passthrough for ARM64.
- Networking issues
- Instability in the bridge configuration (bridge0) via NetworkManager.
- Issues with DHCP and manually adding interfaces to the bridge (NO-CARRIER).
- Cockpit does not always correctly recognize network parameters in ARM64 virtual machines.
Overall, KVM on Raspberry Pi is more of an experimental platform than a stable option for productive use. It is great for research in the field of virtualization on low-power ARM devices, but due to technical limitations and instability, it is difficult to recommend it for real-world deployments.