Skip to content

Lightweight hypervisor

When managing servers either in a small shop or even at scale, Cockpit is a great solution. It even comes pre-installed on various fedora based distro's.

Today I will show you how to make a lightweight hypervisor out of your linux server by using the native qemu with virsh & cockpit as a management gui

Install Cockpit

Install Cockpit for server management

Cockpit is the best choice for a modern web-based UI that’s actively maintained and easy to use.

bash
sudo apt update
sudo apt install cockpit cockpit-machines -y
sudo systemctl enable --now cockpit

Once installed, you can access the UI via:
👉 https://your-server-ip:9090

Configure VM with local lan IP

Overview

To put a VM directly on to the hosts LAN, we need to:

  1. Create a bridge (br0) on the host.
  2. Attach the physical interface (enp0s31f6 or whatever the actual NIC is) to the bridge.
  3. Attach the VM to br0.

Step 1: Create a Real Bridge (br0)

Find the physical interface name (e.g., enp0s31f6):

bash
ip link show

Now, create a Netplan config:

bash
sudo nano /etc/netplan/51-net-bridge-cfg.yaml

Replace its contents with:

yaml
network:
  version: 2
  bridges:
    br0:
      interfaces:
        - enp0s31f6
      dhcp4: yes
      parameters:
        stp: false
        forward-delay: 0

Summary:

  • Creates br0 as a bridge.
  • Attaches enp0s31f6 (our physical interface).
  • Disables STP for faster networking.
  • Gets an IP via DHCP, just like a real LAN device.

Apply the new network config:

bash
sudo chmod 600 /etc/netplan/51-net-bridge-cfg.yaml
sudo netplan apply

Confirm the bridge exists:

bash
ip a show br0

You should see it with an IP on your LAN.


Step 2: Attach the VM to br0

Run virt-install and use the existing br0 bridge:

bash
sudo virt-install \
--name ubuntu-noble \
--memory 10000 \
--vcpus 4 \
--disk path=/home/sysadmin/cluster-mode/build/packer/ubuntu-cloud-image/output-noble/ubuntu-noble.img,format=qcow2 \
--os-variant ubuntu24.04 \
--import \
--network bridge=br0,model=virtio \
--noautoconsole

Step 3: Restart the VM

bash
sudo virsh shutdown ubuntu-noble
sudo virsh start ubuntu-noble

Check vm status

bash
sudo virsh list --all

once the vm is up an running we need to configure the interface inside the vm.

Use virsh console to access vm

If your VM has a serial console configured, you can access it with:

bash
virsh console <vm-name>

Press Enter after connecting to see the login prompt. To exit, use Ctrl+].


Step 4: Configure the networking interface on the virtual machine

Run:

bash
sudo nano /etc/netplan/00-installer-config.yaml

Paste this:

yaml
network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: true
      dhcp6: false
      optional: true
      nameservers:
        addresses:
          - 1.1.1.1

This does the following:

  • Enables DHCP for IPv4 (dhcp4: true).
  • Marks the interface as optional, so the VM won’t hang on boot if DHCP is slow.
  • Uses Cloudflare DNS.

Apply the Configuration

Run:

bash
chmod 600 /etc/netplan/00-installer-config.yaml
sudo netplan apply

Configuration Script

You can copy the snippet below to apply the configurations we just described. Name servers are omitted because we handle them on the network level and dhcp will configure it.

Host script

TODO

VM script

bash
cat <<EOF | sudo tee /etc/netplan/00-installer-config.yaml
network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: true
      optional: true
EOF
chmod 600 /etc/netplan/00-installer-config.yaml
sudo netplan apply

Inside the VM, check its new IP:

bash
ip a

you should now see it with an ip on the hosts lan.

Troubleshooting

Manually Bring Up the Interface Inside the VM

Try forcing the interface up inside the VM:

bash
sudo ip link set enp1s0 up
sudo dhclient enp1s0

Check again:

bash
ip a

If dhclient successfully gets an IP, Libvirt’s DHCP is working.

Manually Request an IP

If you still don’t get an IP, force DHCP to assign one:

bash
sudo dhclient enp1s0

Now check again:

bash
ip a

Find the VM's IP

After booting the VM, check its assigned IP:

bash
sudo virsh domifaddr ubuntu-noble

Example output:

 Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 vnet0      52:54:00:6b:3c:21    ipv4         192.168.122.50/24

Now, SSH into the VM:

If domifaddr doesn’t show an IP, access the VM console and run:

bash
ip a

Configure VM with virtual IP - UNVERIFIED

Check If virbr0 is Providing IPs

Run:

bash
sudo virsh net-list --all

If you see:

 Name      State    Autostart    Persistent
--------------------------------------------
 default   active   yes          yes

Then your default network is active and working.

If default is inactive, start it:

bash
sudo virsh net-start default
sudo virsh net-autostart default

Check If Libvirt's DHCP Is Working

Run this on the host machine:

bash
sudo virsh net-dumpxml default | grep -i range

Expected output:

xml
<range start='192.168.122.2' end='192.168.122.254'/>

If nothing appears, the DHCP server is not running.

Restart the default network:

bash
sudo virsh net-destroy default
sudo virsh net-start default

Then, check if DHCP is handing out leases:

bash
sudo cat /var/lib/libvirt/dnsmasq/default.leases

If the file is empty, Libvirt’s DHCP is failing.

Attach the VM to virbr0

Run virt-install and use the existing virbr0 bridge:

bash
sudo virt-install \
--name ubuntu-noble \
--memory 10000 \
--vcpus 4 \
--disk path=/home/sysadmin/cluster-mode/build/packer/ubuntu-cloud-image/output-noble/ubuntu-noble.img,format=qcow2 \
--os-variant ubuntu24.04 \
--import \
--network bridge=virbr0,model=virtio \
--noautoconsole
  • Network bridge=virbr0: use bridged mode to give the vm a native ip on the local lan.

dump the config with

bash
sudo virsh dumpxml ubuntu-noble > my-vm.xml
# import with: sudo virsh define my-vm.xml

Inside the VM, Replace Netplan Config

Run:

bash
sudo nano /etc/netplan/00-installer-config.yaml

Paste this:

yaml
network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: true
      dhcp6: false
      optional: true
      nameservers:
        addresses:
          - 8.8.8.8
          - 1.1.1.1

🔹 This does the following:

  • Enables DHCP for IPv4 (dhcp4: true).
  • Disables IPv6 (dhcp6: false) since virbr0 usually doesn’t provide it.
  • Marks the interface as optional, so the VM won’t hang on boot if DHCP is slow.
  • Uses Google & Cloudflare DNS.

Apply the Configuration

Run:

bash
sudo netplan apply

Now, check your network:

bash
ip a

If everything is correct, you should see an IP like 192.168.122.X.


Alternative: Assign the VM a Static IP

If you want the VM to have a fixed IP, modify the network configuration inside the VM (/etc/netplan/ for Ubuntu).

Example netplan config inside the VM:

yaml
network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: no
      addresses:
        - 192.168.122.100/24
      gateway4: 192.168.122.1
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]

Then apply:

bash
sudo netplan apply

Now, the VM will always use 192.168.122.100.


Solution: Use iptables to Forward Ports

Since Libvirt uses NAT for virbr0, the VM is behind a virtual subnet (192.168.122.x). We’ll forward ports from the host to the VM, so you can access it like it's on your LAN.

Find Your VM's IP

Run this on the host:

bash
sudo virsh domifaddr ubuntu-noble

Example output:

 Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 vnet0      52:54:00:6b:3c:21    ipv4         192.168.122.50/24

👉 Take note of the VM’s IP (192.168.122.50).


To operate the vm with virsh some example commands

shell
sudo virsh start <vm-name>           # start
sudo virsh shutdown <vm-name>        # graceful shutdown
sudo virsh destroy <vm-name>         # forced shutdown
sudo virsh console <vm-name>         # access the console
sudo virsh undefine <vm-name>        # fully remove VM