When managing virtual machines with KVM/libvirt, you might want to enforce static IP configurations from the host rather than configuring each guest individually. This becomes particularly useful when:
- Managing large VM deployments
- Needing consistent network configurations across environments
- Automating VM provisioning
In bridge mode (the most common production setup), you have two approaches:
1. <interface type='bridge'>
<mac address='52:54:00:xx:xx:xx'/>
<source bridge='br0'/>
<model type='virtio'/>
</interface>
2. <interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>
Modify your libvirt network definition:
sudo virsh net-edit default
<network>
<name>default</name>
<bridge name='virbr0'/>
<forward/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='52:54:00:xx:xx:xx' name='vm1' ip='192.168.122.100'/>
</dhcp>
</ip>
</network>
Restart the network and VM:
sudo virsh net-destroy default
sudo virsh net-start default
sudo virsh reboot vm1
For more control, create a dedicated network:
cat > static-net.xml <<EOF
<network>
<name>static-vm-net</name>
<bridge name='virbr1'/>
<forward mode='bridge'/>
<ip address='10.0.0.1' netmask='255.255.255.0'>
<static>
<mac address='52:54:00:xx:xx:xx'/>
<ip address='10.0.0.100'/>
</static>
</ip>
</network>
EOF
sudo virsh net-define static-net.xml
sudo virsh net-start static-vm-net
For Windows VMs, you'll need to:
- Install virtio-net drivers
- Set the MAC address in the VM configuration to match your static assignment
- Use this PowerShell command in the guest if needed:
Set-NetIPInterface -InterfaceAlias "Ethernet" -Dhcp Disabled
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 10.0.0.100 -PrefixLength 24 -DefaultGateway 10.0.0.1
- Check MAC address consistency between VM config and network definition
- Verify network filters aren't blocking traffic (virsh nwfilter-list)
- Inspect DHCP leases:
cat /var/lib/libvirt/dnsmasq/default.leases
When working with KVM/libvirt in bridge mode, the virtualization host acts as a network bridge connecting virtual machines to the physical network. The traditional approach involves configuring network settings within each guest OS, but there are scenarios where host-side configuration is preferable:
- Centralized management of VM network configurations
- Automation through infrastructure-as-code tools
- Pre-boot network configuration
- Standardization across multiple VM deployments
There are two primary approaches to achieve static IP assignment from the host:
Method 1: Using DHCP Reservations
This method leverages libvirt's built-in DHCP server capabilities:
# Edit the network definition
virsh net-edit default
# Add the following inside the <network> tag
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='52:54:00:00:00:01' name='vm1' ip='192.168.122.100'/>
</dhcp>
</ip>
Method 2: Direct Interface Configuration
For more control, configure the interface directly in the VM's XML:
virsh edit vm1
# Add/modify the interface section
<interface type='bridge'>
<mac address='52:54:00:00:00:01'/>
<source bridge='br0'/>
<protocol family='ipv4'>
<ip address='192.168.1.100' prefix='24'/>
<route gateway='192.168.1.1'/>
</protocol>
</interface>
For Windows VMs, additional steps may be required to ensure proper network initialization:
# In the VM XML definition, add:
<metadata>
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
<libosinfo:os id="http://microsoft.com/win/10"/>
</libosinfo:libosinfo>
</metadata>
<clock offset='localtime'/>
<features>
<acpi/>
<apic/>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
</features>
When configuration doesn't take effect:
- Restart the libvirt network:
virsh net-destroy default && virsh net-start default
- Verify guest agent is installed and running
- Check for MAC address conflicts
- Ensure the bridge interface is properly configured on the host
For automated deployments, consider this bash script snippet:
#!/bin/bash
VM_NAME="vm1"
IP_ADDR="192.168.1.100"
GATEWAY="192.168.1.1"
MAC="52:54:00:00:00:01"
virsh dumpxml $VM_NAME > /tmp/$VM_NAME.xml
xmlstarlet ed -L \
-u "/domain/devices/interface[@type='bridge']/mac/@address" -v "$MAC" \
-s "/domain/devices/interface[@type='bridge']" -t elem -n "protocol" -v "" \
-s "/domain/devices/interface[@type='bridge']/protocol" -t elem -n "ip" -v "" \
-i "/domain/devices/interface[@type='bridge']/protocol/ip" -t attr -n "address" -v "$IP_ADDR" \
-i "/domain/devices/interface[@type='bridge']/protocol/ip" -t attr -n "prefix" -v "24" \
-s "/domain/devices/interface[@type='bridge']/protocol" -t elem -n "route" -v "" \
-i "/domain/devices/interface[@type='bridge']/protocol/route" -t attr -n "gateway" -v "$GATEWAY" \
/tmp/$VM_NAME.xml
virsh define /tmp/$VM_NAME.xml