Setup Bridge Network for a Kubevirt VM
Introduction
Kubevirt VM connect to the cluster CNI (Flannel, Calico, etc), which is perfect for pod-to-pod communication but limits VMs that need full LAN access. We will address that by attaching it to the bridge network on host.
Part of series on Kubevirt
1. Prerequisites
Before diving in, ensure you have:
- A K3s cluster with
kubectl
oroc
access. - Linux bridge configured on host (
bridge0
). jq
installed (sudo dnf install jq
orbrew install jq
).- Basic knowledge of KubeVirt, CDI, and Kubernetes networking.
HLD
Home Network
1.1 Router (192.168.50.1): Provides LAN IPs via DHCP.
1.2 User Device: Laptop/Phone accessing VM or cluster services.Kubernetes Host
2.1 Physical NIC (enp3s0): Connects host to LAN.
2.2 Linux Bridge (bridge0): Virtual switch connecting VMs to LAN.
2.3 Flow:NIC → Bridge → VM → LAN
Kubernetes Cluster
3.1 Multus CNI: Manages VM network attachments.
3.2 KubeVirt VM (Fedora): Attaches tobridge0
viahomenet
NAD.Network Flow
4.1 VM gets LAN IP via DHCP.
4.2VM ↔ Linux Bridge ↔ Host NIC ↔ Router ↔ LAN
4.3 VM can communicate directly with user devices.Key Points
5.1 VM behaves like a normal LAN device.
5.2 LAN devices can access VM directly.
5.3 Multus + bridge allows bypassing pod network NAT.
graph TD
%% Home Network
subgraph "Home Network"
Router["Router / DHCP Server<br>IP: 192.168.50.1"]
UserDevice["Laptop / Phone"]
end
%% Kubernetes Host
subgraph "Kubernetes Host"
direction LR
HostNIC["Physical NIC: enp3s0"]
LinuxBridge["Linux Bridge: bridge0"]
HostNIC -->|Port added to| LinuxBridge
end
%% Kubernetes Cluster
subgraph "Kubernetes Cluster"
Multus["Multus CNI"]
KubeVirtVM["KubeVirt VM - Fedora"]
end
%% Network Flow
Router -->|DHCP Lease / LAN Traffic| HostNIC
LinuxBridge -->|Virtual Port| Multus
Multus -->|Attaches VM to bridge - NAD homenet| KubeVirtVM
KubeVirtVM -->|Traffic to/from LAN| LinuxBridge
KubeVirtVM -->|Gets LAN IP| Router
KubeVirtVM -->|Communicates with| UserDevice
UserDevice -->|Connects to| KubeVirtVM
%% Styles
style Router fill:#fcf,stroke:#333,stroke-width:2px
style UserDevice fill:#cff,stroke:#333,stroke-width:2px
style HostNIC fill:#ffc,stroke:#333,stroke-width:2px
style LinuxBridge fill:#bbf,stroke:#333,stroke-width:2px
style Multus fill:#ff9,stroke:#333,stroke-width:2px
style KubeVirtVM fill:#9f9,stroke:#333,stroke-width:2px
2. Why use a Bridge Network for KubeVirt VMs
KubeVirt VMs normally connect to the cluster CNI (Flannel, Calico, etc), which is perfect for pod-to-pod communication but limits VMs that need:
- LAN visibility – VM gets an IP on the same subnet as your home devices.
- Direct device access – Talk to IoT devices, NAS, printers without NAT.
- Predictable IPs – DHCP or static assignment for consistent network identity.
Solution: Multus + bridge CNI. The VM attaches to bridge0
on the host, making it a full LAN citizen.
Process flow:
graph TB
A["Create Linux Bridge on Host"] --> B["Install KubeVirt & CDI"]
B --> C["Install Multus"]
C --> D["Create NetworkAttachmentDefinition"]
D --> E["Deploy VM with Network Attachment"]
3. Host Linux Bridge Setup (bridge0
) with NetworkManager
bridge0
is a virtual switch; the physical interface (enp3s0
) connects it to your LAN. VMs attach to bridge0
via Multus.
CLI Steps
# 3.1 Create the bridge
nmcli connection add type bridge con-name bridge0 ifname bridge0 stp no
# 3.2 Add physical NIC to bridge
nmcli connection add type ethernet con-name "Wired Connection" ifname enp3s0 master bridge0
# 3.3 Configure DHCP (or static if desired)
nmcli connection modify bridge0 ipv4.method auto
nmcli connection modify bridge0 ipv6.method auto
# 3.4 Enable autoconnect
nmcli connection modify bridge0 connection.autoconnect yes
nmcli connection modify "Wired Connection" connection.autoconnect yes
# 3.5 Bring up connections
nmcli connection up bridge0
nmcli connection up "Wired Connection"
# 3.6 Verify setup
nmcli connection show
nmcli device status
✅ Result: bridge0
is live, VMs attached will get LAN IPs automatically.
💡 Tip: Use nmcli
on headless servers for fully automated bridge setup.
4. Install KubeVirt
Deploy the operator and KubeVirt CR to run VMs on Kubernetes.
export KUBEVIRT_VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | jq -r .tag_name)
echo $KUBEVIRT_VERSION
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/$KUBEVIRT_VERSION/kubevirt-operator.yaml
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/$KUBEVIRT_VERSION/kubevirt-cr.yaml
kubectl get pods -n kubevirt
kubevirt
namespace are Running before continuing.5. Install Containerized Data Importer (CDI)
CDI handles VM image uploads and DataVolumes.
export CDI_VERSION=$(curl -s https://api.github.com/repos/kubevirt/containerized-data-importer/releases/latest | jq -r .tag_name)
echo $CDI_VERSION
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-cr.yaml
kubectl get pods -n cdi
# kubectl -n cdi port-forward svc/cdi-uploadproxy 8443:443
6. Install Multus on K3s
Multus enables multiple interfaces per VM/pod—essential for bridge networking.
cat << EOF | oc apply -f-
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: multus
namespace: kube-system
spec:
repo: https://rke2-charts.rancher.io
chart: rke2-multus
targetNamespace: kube-system
valuesContent: |-
config:
fullnameOverride: multus
cni_conf:
confDir: /var/lib/rancher/k3s/agent/etc/cni/net.d
binDir: /var/lib/rancher/k3s/data/cni/
kubeconfig: /var/lib/rancher/k3s/agent/etc/cni/net.d/multus.d/multus.kubeconfig
multusAutoconfigDir: /var/lib/rancher/k3s/agent/etc/cni/net.d
manifests:
dhcpDaemonSet: true
EOF
7. Create DataVolume (Fedora 42)
Create a persistent VM disk.
cat << EOF | oc apply -f-
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
annotations:
cdi.kubevirt.io/storage.bind.immediate.requested: ""
name: fedora-dv-42
spec:
contentType: kubevirt
source:
http:
url: "https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2"
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
EOF
8. Network Attachment Definition
Attach VM to bridge0
. You can choose DHCP or Static IP.
cat << EOF | oc apply -f-
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: homenet
spec:
config: '{
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "bridge0",
"ipam": { "type": "dhcp" }
}'
EOF
NAD diagram
graph TD
NAD_Choice{{"Network Attachment Definition<br>(homenet)"}}
subgraph "Option 1: Dynamic IP"
DHCP["IPAM: DHCP"]
DHCP_Config[("ipam: { type: dhcp }")]
NAD_Choice -->|Selects| DHCP
DHCP -->|Uses CNI Plugin| DHCP_Config
end
subgraph "Option 2: Static IP"
Static["IPAM: Static"]
Static_Config[("ipam: { type: static, addresses: [...] }")]
NAD_Choice -->|Selects| Static
Static -->|Specifies configuration| Static_Config
end
style NAD_Choice fill:#fff,stroke:#333,stroke-width:2px
9. Create VirtualMachine
Deploy Fedora VM with homenet
interface.
cat << EOF | oc apply -f-
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: fedora-dv-bridge
labels:
kubevirt.io/os: linux
spec:
runStrategy: Always
template:
metadata:
labels:
kubevirt.io/domain: vm
spec:
domain:
cpu:
cores: 1
devices:
disks:
- name: disk0
disk:
bus: virtio
- name: cloudinitdisk
cdrom:
bus: sata
readonly: true
interfaces:
- name: homenet
bridge: {}
model: virtio
machine:
type: q35
resources:
requests:
memory: 2048M
networks:
- name: homenet
multus:
networkName: homenet
volumes:
- name: disk0
persistentVolumeClaim:
claimName: fedora-dv-42
- name: cloudinitdisk
cloudInitNoCloud:
userData: |
#cloud-config
hostname: fedora-dv-bridge
ssh_pwauth: True
password: fedora
chpasswd: {expire: False}
runcmd:
- dnf install -y qemu-guest-agent cockpit
- systemctl enable qemu-guest-agent
- systemctl enable --now cockpit.socket
- systemctl start qemu-guest-agent cockpit
EOF
🎉 Congratulations! Your KubeVirt VM now has a LAN IP, can SSH in, use Cockpit, and interact with home network devices seamlessly.