Skip to content

KVM Virtualization (Networking)


KVM Virtualization Tutorial 2 for Debian and Ubuntu Server.

Please refer to the KVM Virtualization Beginner tutorial before reading this one.

This tutorial focuses on KVM networking configurations.

  • Server (Host): Debian (13/12) or Ubuntu (24.04/22.04) Server
    • IP: 192.168.1.121
    • Name: elma
    • NIC1: enp3s0f0
    • NIC2: enx00e04c534458 (USB network adapter)
  • Network1: 192.168.1.0/24 (internet modem/router, first interface)
  • Network2: 10.1.1.0/24 (external switch, second interface)
  • Workstation: Debian 13 or Ubuntu 24.04 LTS Desktop
  • ISBN: 978-1-78829-467-6 KVM Virtualization Cookbook by Konstantin Ivanov
  • ISBN: 978-1-83882-871-4 Mastering KVM Virtualization 2nd Ed. by Vedran Dakic, Humble Devassy Chirammal, Prasad Mukhedkar, Anil Vettathu


While numerous variations exist, KVM supports three basic network types:

  • Bridged: VMs appear as physical devices on the network
  • NAT: VMs share host’s IP with network address translation
  • Isolated: VMs communicate only with each other and host

List KVM Networks:

Terminal window
virsh net-list

Example output (after Tutorial 1 bridge configuration):

Name State Autostart Persistent
-------------------------------------------------
host-bridge active yes yes

Display detailed network information:

Terminal window
virsh net-info NETWORKNAME
virsh net-info host-bridge

Example output:

Name: host-bridge
UUID: a67dfcef-86e9-4e4c-832f-bc14443da475
Active: yes
Persistent: yes
Autostart: yes
Bridge: br0

Display network configuration as XML:

Terminal window
virsh net-dumpxml NETWORKNAME
virsh net-dumpxml host-bridge

Example output:

<network>
<name>host-bridge</name>
<uuid>a67dfcef-86e9-4e4c-832f-bc14443da475</uuid>
<forward mode='bridge'/>
<bridge name='br0'/>
</network>

To add a network, create an XML configuration file and define it:

Terminal window
virsh net-define XMLFILE

Example: Create a second bridge on the second interface.

First, create the bridge on the host system.

Edit Netplan configuration:

Terminal window
sudo nano /etc/netplan/01-netcfg.yaml

Replace content with (adjust interface names and IPs):

network:
ethernets:
enp3s0f0:
dhcp4: false
dhcp6: false
enx00e04c534458:
dhcp4: false
dhcp6: false
bridges:
br0:
interfaces: [ enp3s0f0 ]
addresses: [192.168.1.121/24]
routes:
- to: default
via: 192.168.1.1
mtu: 1500
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
parameters:
stp: true
forward-delay: 4
dhcp4: false
dhcp6: false
br1:
interfaces: [ enx00e04c534458 ]
addresses: [10.1.1.1/24]
routes:
- to: 10.1.1.0/24
via: 10.1.1.1
mtu: 1500
nameservers:
addresses: [8.8.8.8,8.8.4.4]
dhcp4: false
dhcp6: false
version: 2

Apply configuration:

Terminal window
sudo netplan apply

Edit network interfaces file:

Terminal window
sudo nano /etc/network/interfaces

Replace content with (adjust interface names and IPs):

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
# The primary network interface
auto enp3s0f0
#make sure we don't get addresses on our raw device
iface enp3s0f0 inet manual
#set up bridge and give it a static ip
auto br0
iface br0 inet static
address 192.168.1.121
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
bridge_ports enp3s0f0
bridge_stp off
bridge_fd 0
bridge_maxwait 0
dns-nameservers 8.8.8.8
auto br1
iface br1 inet static
address 10.1.1.1
netmask 255.255.255.0
network 10.1.1.0
broadcast 10.1.1.255
bridge_ports enx00e04c534458
bridge_stp off
bridge_fd 0
bridge_maxwait 0

Apply configuration (SSH may disconnect):

Terminal window
sudo systemctl restart networking.service

Create XML configuration for the second bridge:

Terminal window
sudo nano host-bridge2.xml

Fill as below:

<network>
<name>host-bridge2</name>
<uuid>c723a80b-d496-460e-9235-9eced7b218cf</uuid>
<forward mode='bridge'/>
<bridge name='br1'/>
</network>

Note: Generate a unique UUID with uuidgen if needed.

Define, start, and enable autostart:

Terminal window
virsh net-define host-bridge2.xml
virsh net-start host-bridge2
virsh net-autostart host-bridge2

Now you have two bridges:

  • br0 for 192.168.1.0/24 network
  • br1 for 10.1.1.0/24 network

Stop a KVM Network:

Terminal window
virsh net-destroy NETWORKNAME
virsh net-destroy host-bridge2

Disable autostart:

Terminal window
virsh net-autostart NETWORKNAME --disable
virsh net-autostart host-bridge2 --disable

Remove network definition:

Terminal window
virsh net-undefine NETWORKNAME
virsh net-undefine host-bridge2


When creating KVM network XML files, use unique UUID and MAC values for each network.

Generate a random UUID:

Terminal window
uuidgen

Generate a random MAC address (or use online tools like Browserling Random MAC Generator).

Bridged networks connect VMs directly to the host’s physical network. VMs appear as separate devices on the same network segment as the host.

Use cases:

  • Public-facing servers
  • Services requiring direct network access
  • Environments with existing DHCP infrastructure

Requirements:

  1. Create a bridge in the host’s network configuration
  2. Define the bridge in KVM via XML

Example XML configuration:

<network>
<name>host-bridge2</name>
<uuid>c723a80b-d496-460e-9235-9eced7b218cf</uuid>
<forward mode='bridge'/>
<bridge name='br1'/>
</network>

Customization:

  • Replace host-bridge2 with your network name
  • Replace UUID with a generated value
  • Replace br1 with your actual bridge interface name

Network integration:

  • VMs receive IPs from the same DHCP server as the host
  • VMs are directly accessible from the network
  • No NAT translation occurs

NAT (Network Address Translation) networks allow VMs to access external networks while remaining hidden behind the host’s IP address.

Use cases:

  • Development and testing environments
  • VMs requiring internet access but not public exposure
  • Security-focused deployments

Example XML configuration:

<network>
<name>nat</name>
<uuid>d589efd6-7d61-4f92-976b-bde62956cca7</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='brnat' stp='on' delay='0'/>
<mac address='4a:c3:6a:72:c2:30'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.101' end='192.168.122.254'/>
</dhcp>
</ip>
</network>

Customization:

  • Replace nat with your network name
  • Replace UUID with a generated value
  • Replace brnat with your preferred bridge name
  • Replace MAC address with a generated value
  • Adjust IP subnet and DHCP range as needed

Network behavior:

  • VMs can initiate connections to external networks
  • External hosts cannot initiate connections to VMs
  • Port forwarding can be configured for specific services
  • Built-in DHCP server provides IP addresses

Isolated networks create completely private segments where VMs can communicate only with each other and the host.

Use cases:

  • Database backends
  • Internal service communication
  • Security-sensitive applications
  • Testing environments requiring network isolation

Example XML configuration:

<network>
<name>isolated</name>
<uuid>a67bbbaf-81e9-4e4c-832f-bc14443da475</uuid>
<bridge name='brisolated' stp='on' delay='0'/>
<mac address='4a:c3:6a:72:c2:26'/>
<domain name='myisolateddomain'/>
<ip address='192.168.20.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.20.101' end='192.168.20.200'/>
</dhcp>
</ip>
</network>

Customization:

  • Replace isolated with your network name
  • Replace UUID with a generated value
  • Replace brisolated with your preferred bridge name
  • Replace MAC address with a generated value
  • Adjust IP subnet and DHCP range as needed
  • Set domain name for internal DNS

Security architecture example:

Internet ── [Web Server (dual NIC)] ── [Database Server]
bridged:192.168.1.x/24 isolated:192.168.20.x/24

Implementation:

  1. Web server: One interface in bridged network, one in isolated network
  2. Database server: Only isolated network interface
  3. Result: Database accessible only via web server

Network characteristics:

  • No external network access
  • Built-in DHCP server
  • Internal DNS resolution via domain name
  • Complete isolation from external traffic

3. Case Study A: Bridged and Isolated Networks Together

Section titled “3. Case Study A: Bridged and Isolated Networks Together”

  • Network1: Bridged network on 192.168.1.0/24 (existing)
  • Network2: Isolated network on 192.168.20.0/24 (to create)
  • VM1: Two network interfaces
    • Interface 1: Network1 (bridged)
    • Interface 2: Network2 (isolated)
  • VM2: One network interface
    • Interface: Network2 (isolated)

Architecture:

  • VM1: Accessible from all network devices (via Network1)
  • VM2: Accessible only from VM1 (via Network2)
  • VM1 acts as a gateway between the two networks

Create XML configuration file:

Terminal window
nano isolated.xml

Fill as below:

<network>
<name>isolated</name>
<uuid>a67bbbaf-81e9-4e4c-832f-bc14443da475</uuid>
<bridge name='brisolated' stp='on' delay='0'/>
<mac address='4a:c3:6a:72:c2:26'/>
<domain name='myisolateddomain'/>
<ip address='192.168.20.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.20.101' end='192.168.20.200'/>
</dhcp>
</ip>
</network>

Define and activate the network:

Terminal window
virsh net-define isolated.xml
virsh net-start isolated
virsh net-autostart isolated

Verify network creation:

Terminal window
virsh net-list --all

3.3. Create VMs with Dual Network Configuration

Section titled “3.3. Create VMs with Dual Network Configuration”

Create VM1 (dual-homed):

Terminal window
sudo virt-install --name vm1 \
--connect qemu:///system --virt-type kvm \
--memory 1024 --vcpus 1 \
--disk /srv/kvm/vm1.qcow2,format=qcow2,size=10 \
--cdrom /srv/isos/ubuntu-24.04.2-live-server-amd64.iso \
--network bridge=br0 \
--network bridge=brisolated \
--graphics vnc,port=5901,listen=0.0.0.0 \
--os-variant ubuntu22.04 \
--noautoconsole

Create VM2 (isolated):

Terminal window
sudo virt-install --name vm2 \
--connect qemu:///system --virt-type kvm \
--memory 1024 --vcpus 1 \
--disk /srv/kvm/vm2.qcow2,format=qcow2,size=10 \
--cdrom /srv/isos/ubuntu-24.04.2-live-server-amd64.iso \
--network bridge=brisolated \
--graphics vnc,port=5902,listen=0.0.0.0 \
--os-variant ubuntu22.04 \
--noautoconsole

Note for Debian 12 hosts: Replace --os-variant ubuntu24.04 with --os-variant ubuntu22.04.

Connect to VMs from your workstation to complete installation:

Isolated Network Limitations:

  • VM2 cannot access external networks (including internet)
  • VM2 cannot receive updates or install packages directly
  • VM1 can access both networks and act as a gateway

You can install Squid proxy on the host to provide internet access for isolated VMs.


4. Case Study B: Separating Host and VM Access with 2 NICs

Section titled “4. Case Study B: Separating Host and VM Access with 2 NICs”

Separating host and VM network traffic is a recommended practice for improved security, performance, and management. We’ll dedicate one interface for host access and another for VM traffic.

  • Network: 192.168.1.0/24 (single subnet)
  • Host Interfaces:
    • NIC1 (enp3s0f0): VM traffic via bridge (192.168.1.121)
    • NIC2 (enx00e04c534458): Host management (192.168.1.122)
  • VM Configuration: Single interface on bridged network

Benefits:

  • Isolate host management traffic from VM traffic
  • Prevent VM network issues from affecting host access
  • Simplified firewall rules and monitoring
  • Better performance isolation

Configure the server’s network interfaces before KVM setup.

Edit Netplan configuration:

Terminal window
sudo nano /etc/netplan/01-netcfg.yaml

Change as below:

network:
ethernets:
enp3s0f0:
dhcp4: false
dhcp6: false
enx00e04c534458:
addresses: [192.168.1.122/24]
routes:
- to: 192.168.1.0/24
via: 192.168.1.1
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
bridges:
br0:
interfaces: [ enp3s0f0 ]
addresses: [192.168.1.121/24]
routes:
- to: default
via: 192.168.1.1
mtu: 1500
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
dhcp4: false
dhcp6: false
version: 2

Apply configuration and reboot:

Terminal window
sudo netplan apply
sudo reboot

Edit network interfaces file:

Terminal window
sudo nano /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
# VM traffic interface (bridged)
auto enp3s0f0
iface enp3s0f0 inet manual
# Bridge for VMs
auto br0
iface br0 inet static
address 192.168.1.121
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
bridge_ports enp3s0f0
bridge_stp off
bridge_fd 0
bridge_maxwait 0
dns-nameservers 8.8.8.8
# Host management interface
auto enx00e04c534458
iface enx00e04c534458 inet static
address 192.168.1.122
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
dns-nameservers 8.8.8.8

Apply configuration (SSH may disconnect):

Terminal window
sudo systemctl restart networking.service

Note: You may need to reboot because of the previous network configurations may cause instability.

Network Verification:

Terminal window
ip addr show br0
ip addr show enx00e04c534458
ping -c 3 192.168.1.1

Skip this step if host-bridge already exists. Define the bridged network in KVM:

Create XML configuration:

Terminal window
nano host-bridge.xml

Fill as below:

<network>
<name>host-bridge</name>
<forward mode="bridge"/>
<bridge name="br0"/>
</network>

Define and activate network:

Terminal window
virsh net-define host-bridge.xml
virsh net-start host-bridge
virsh net-autostart host-bridge

Verify network creation:

Terminal window
virsh net-list --all
virsh net-info host-bridge

Create a VM using the bridged interface:

Terminal window
sudo virt-install --name vm3 \
--connect qemu:///system --virt-type kvm \
--memory 1024 --vcpus 1 \
--disk /srv/kvm/vm3.qcow2,format=qcow2,size=10 \
--cdrom /srv/isos/ubuntu-24.04.2-live-server-amd64.iso \
--network bridge=br0 \
--graphics vnc,port=5901,listen=0.0.0.0 \
--os-variant ubuntu22.04 \
--noautoconsole

Connect to VM from your workstation:

Terminal window
virt-viewer --connect qemu+ssh://exforge@elma/system vm3

Network Verification:

  1. Host should be accessible at 192.168.1.122
  2. VM should obtain an IP from 192.168.1.0/24 range
  3. Both should have internet access via 192.168.1.1

Firewall Considerations: Consider implementing firewall rules to:

  • Restrict host management interface access
  • Separate VM traffic rules
  • Monitor traffic patterns


We will create a VM, in a NAT network.

Create a VM within a NAT (Network Address Translation) network for enhanced security and isolation.

Server Configuration:

  • Interface 1 (enp3s0f0): Bridged mode for VMs (as configured in Section 4)
  • Interface 2 (enx00e04c534458): Standard mode for host management
  • Additional NAT network for isolated VMs

VM Configuration:

  • Name: vmn (VM-NAT)
  • Single interface connected to NAT network
  • Outbound internet access via NAT
  • Inbound connections blocked (except port forwarding)

No changes required if you completed Section 4.2. Verify existing configuration:

Terminal window
ip addr show br0
ip addr show enx00e04c534458

Create XML configuration file:

Terminal window
nano nat.xml

Fill as below:

<network>
<name>nat</name>
<uuid>d589efd6-7d61-4f92-976b-bde62956cca7</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='brnat' stp='on' delay='0'/>
<mac address='4a:c3:6a:72:c2:30'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.101' end='192.168.122.254'/>
</dhcp>
</ip>
</network>

Important: Generate unique values:

  • UUID: uuidgen
  • MAC address: Use a random generator or openssl rand -hex 6 | sed 's/\(..\)/\1:/g; s/.$//'

Define and activate the NAT network:

Terminal window
virsh net-define nat.xml
virsh net-start nat
virsh net-autostart nat

Verify network creation:

Terminal window
virsh net-list --all
virsh net-info nat
virsh net-dumpxml nat

Create the NAT-connected VM:

Terminal window
sudo virt-install --name vmn \
--connect qemu:///system --virt-type kvm \
--memory 1024 --vcpus 1 \
--disk /srv/kvm/vmn.qcow2,format=qcow2,size=10 \
--cdrom /srv/isos/ubuntu-24.04.2-live-server-amd64.iso \
--network bridge=brnat \
--graphics vnc,port=5902,listen=0.0.0.0 \
--os-variant ubuntu22.04 \
--noautoconsole

Note: For Debian 12 hosts, use --os-variant ubuntu22.04.

Outbound Access (VM → External):

  • VM can initiate connections to external networks
  • Source IP translated to host’s IP (192.168.1.121)
  • Source ports mapped to 1024-65535 range

Inbound Access (External → VM):

  • External devices cannot initiate connections to VM
  • Port forwarding required for specific services
  • VM remains hidden behind NAT

Internal Network:

  • DHCP range: 192.168.122.101-254
  • Gateway: 192.168.122.1 (host)
  • DNS: Inherited from host configuration

6. Adding and Removing Networks To/From a VM

Section titled “6. Adding and Removing Networks To/From a VM”

Dynamically modify VM network interfaces without recreating the VM.

  • VM Name: vmtest
  • Initial Network: host-bridge (bridge br0)
  • Network to Add: nat (bridge brnat)
  • Network to Remove: host-bridge (bridge br0)

Process:

  1. Create VM with bridged network
  2. Add NAT network interface
  3. Configure new interface on VM
  4. Remove original bridged interface

Create initial VM:

Terminal window
sudo virt-install --name vmtest \
--connect qemu:///system --virt-type kvm \
--memory 1024 --vcpus 1 \
--disk /srv/kvm/vm.qcow2,format=qcow2,size=10 \
--cdrom /srv/isos/ubuntu-24.04.2-live-server-amd64.iso \
--network bridge=br0 \
--graphics vnc,port=5902,listen=0.0.0.0 \
--os-variant ubuntu22.04 \
--noautoconsole

Add a second network interface to the running VM:

Terminal window
virsh attach-interface vmtest \
bridge brnat \
--target ens1 \
--config

Parameters:

  • bridge brnat: Network type and bridge name
  • --target ens1: Interface name inside VM
  • --config: Persistent after shutdown/start

Restart VM (required for interface activation):

Terminal window
virsh destroy vmtest
virsh start vmtest

Important: virsh reboot may not activate new interfaces; use shutdown/start cycle.

6.4. Configure New Network Interface on VM

Section titled “6.4. Configure New Network Interface on VM”

Configure the new interface (ens1) on the Ubuntu VM.

On the VM:

Check available interfaces:

Terminal window
ip link show

Configure Netplan (Ubuntu):

Terminal window
sudo nano /etc/netplan/00-installer-config.yaml
network:
ethernets:
enp1s0:
dhcp4: true
ens1:
dhcp4: true
version: 2

For Debian VMs, use `/etc/network/interfaces**:

Terminal window
sudo nano /etc/network/interfaces
Terminal window
auto lo
iface lo inet loopback
auto enp1s0
iface enp1s0 inet dhcp
auto ens1
iface ens1 inet dhcp

Apply configuration:

Ubuntu:

Terminal window
sudo netplan apply

Debian:

Terminal window
sudo systemctl restart networking

Verify network configuration:

Terminal window
ip addr show
ip route show

Remove the original bridged interface (enp1s0).

On the VM:

Identify MAC address of interface to remove:

Terminal window
ip link show enp1s0

Example output:

2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:83:3c:a0 brd ff:ff:ff:ff:ff:ff

MAC Address: 52:54:00:83:3c:a0

On the Host:

Detach interface using MAC address:

Terminal window
virsh detach-interface vmtest \
bridge \
--mac 52:54:00:f7:6d:66 \
--config

Parameters:

  • bridge: Network type
  • --mac: MAC address of interface to remove
  • --config: Persistent after shutdown/start

For immediate removal (if VM is running):

Terminal window
virsh detach-interface vmtest \
bridge \
--mac 52:54:00:83:3c:a0 \
--live \
--config

Shutdown and start VM (not reboot) to complete removal:

Terminal window
virsh destroy vmtest
virsh start vmtest